From 7b271eba888de89c075f5372dd1cd1f48eeecb05 Mon Sep 17 00:00:00 2001 From: David Li Date: Thu, 14 Apr 2011 11:20:22 -0700 Subject: [PATCH] GLES2Dbg: initial tests Change-Id: Ibf07eff68d39267fd5c9bec2870f59a5ae2f51e3 --- opengl/libs/GLES2_dbg/Android.mk | 2 + opengl/libs/GLES2_dbg/src/dbgcontext.cpp | 37 ++- opengl/libs/GLES2_dbg/src/header.h | 5 +- opengl/libs/GLES2_dbg/src/server.cpp | 5 +- opengl/libs/GLES2_dbg/src/vertex.cpp | 16 +- opengl/libs/GLES2_dbg/test/Android.mk | 39 +++ opengl/libs/GLES2_dbg/test/test_main.cpp | 234 ++++++++++++++ opengl/libs/GLES2_dbg/test/test_server.cpp | 197 ++++++++++++ opengl/libs/GLES2_dbg/test/test_socket.cpp | 474 +++++++++++++++++++++++++++++ 9 files changed, 991 insertions(+), 18 deletions(-) create mode 100644 opengl/libs/GLES2_dbg/test/Android.mk create mode 100644 opengl/libs/GLES2_dbg/test/test_main.cpp create mode 100644 opengl/libs/GLES2_dbg/test/test_server.cpp create mode 100644 opengl/libs/GLES2_dbg/test/test_socket.cpp diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk index 853cce670a..9f6e68c4da 100644 --- a/opengl/libs/GLES2_dbg/Android.mk +++ b/opengl/libs/GLES2_dbg/Android.mk @@ -45,3 +45,5 @@ LOCAL_MODULE:= libGLESv2_dbg LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) + +include $(LOCAL_PATH)/test/Android.mk diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp index fe93874812..71892d3a10 100644 --- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp +++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp @@ -25,11 +25,11 @@ extern "C" namespace android { -static pthread_key_t sEGLThreadLocalStorageKey = -1; +pthread_key_t dbgEGLThreadLocalStorageKey = -1; DbgContext * getDbgContextThreadSpecific() { - tls_t* tls = (tls_t*)pthread_getspecific(sEGLThreadLocalStorageKey); + tls_t* tls = (tls_t*)pthread_getspecific(dbgEGLThreadLocalStorageKey); return tls->dbg; } @@ -63,7 +63,7 @@ DbgContext::~DbgContext() DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey, const unsigned version, const gl_hooks_t * const hooks) { - sEGLThreadLocalStorageKey = EGLThreadLocalStorageKey; + dbgEGLThreadLocalStorageKey = EGLThreadLocalStorageKey; assert(version < 2); assert(GL_NO_ERROR == hooks->gl.glGetError()); GLint MAX_VERTEX_ATTRIBS = 0; @@ -149,6 +149,37 @@ void DbgContext::Compress(const void * in_data, unsigned int in_len, } } +unsigned char * DbgContext::Decompress(const void * in, const unsigned int inLen, + unsigned int * const outLen) +{ + assert(inLen > 4 * 3); + if (inLen < 4 * 3) + return NULL; + *outLen = *(uint32_t *)in; + unsigned char * const out = (unsigned char *)malloc(*outLen); + unsigned int outPos = 0; + const unsigned char * const end = (const unsigned char *)in + inLen; + for (const unsigned char * inData = (const unsigned char *)in + 4; inData < end; ) { + const uint32_t chunkOut = *(uint32_t *)inData; + inData += 4; + const uint32_t chunkIn = *(uint32_t *)inData; + inData += 4; + if (chunkIn > 0) { + assert(inData + chunkIn <= end); + assert(outPos + chunkOut <= *outLen); + outPos += lzf_decompress(inData, chunkIn, out + outPos, chunkOut); + inData += chunkIn; + } else { + assert(inData + chunkOut <= end); + assert(outPos + chunkOut <= *outLen); + memcpy(out + outPos, inData, chunkOut); + inData += chunkOut; + outPos += chunkOut; + } + } + return out; +} + void * DbgContext::GetReadPixelsBuffer(const unsigned size) { if (lzf_refBufSize < size + 8) { diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h index c9e6c41676..f2b1fa6635 100644 --- a/opengl/libs/GLES2_dbg/src/header.h +++ b/opengl/libs/GLES2_dbg/src/header.h @@ -73,8 +73,9 @@ struct GLFunctionBitfield { }; struct DbgContext { -private: static const unsigned int LZF_CHUNK_SIZE = 256 * 1024; + +private: char * lzf_buf; // malloc / free; for lzf chunk compression and other uses // used as buffer and reference frame for ReadPixels; malloc/free @@ -129,6 +130,8 @@ public: void Fetch(const unsigned index, std::string * const data) const; void Compress(const void * in_data, unsigned in_len, std::string * const outStr); + static unsigned char * Decompress(const void * in, const unsigned int inLen, + unsigned int * const outLen); // malloc/free void * GetReadPixelsBuffer(const unsigned size); bool IsReadPixelBuffer(const void * const ptr) { return ptr == lzf_ref[lzf_readIndex]; diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp index f13d6cc849..ba4960dd9a 100644 --- a/opengl/libs/GLES2_dbg/src/server.cpp +++ b/opengl/libs/GLES2_dbg/src/server.cpp @@ -186,7 +186,7 @@ float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd) Die("Failed to send message length"); } nsecs_t c0 = systemTime(timeMode); - sent = send(clientSock, str.c_str(), str.length(), 0); + sent = send(clientSock, str.data(), str.length(), 0); float t = (float)ns2ms(systemTime(timeMode) - c0); if (sent != str.length()) { LOGD("actual sent=%d expected=%d clientSock=%d", sent, str.length(), clientSock); @@ -246,8 +246,9 @@ int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg, msg.set_function(function); // when not exectResponse, set cmd to CONTINUE then SKIP + // cmd will be overwritten by received command cmd.set_function(glesv2debugger::Message_Function_CONTINUE); - cmd.set_expect_response(false); + cmd.set_expect_response(expectResponse); glesv2debugger::Message_Function oldCmd = cmd.function(); Send(msg, cmd); expectResponse = cmd.expect_response(); diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp index 7edc050331..029ee3bf50 100644 --- a/opengl/libs/GLES2_dbg/src/vertex.cpp +++ b/opengl/libs/GLES2_dbg/src/vertex.cpp @@ -43,10 +43,8 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count) void * pixels = NULL; int viewport[4] = {}; - if (!expectResponse) { - cmd.set_function(glesv2debugger::Message_Function_CONTINUE); - cmd.set_expect_response(false); - } + cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + cmd.set_expect_response(expectResponse); glesv2debugger::Message_Function oldCmd = cmd.function(); Send(msg, cmd); expectResponse = cmd.expect_response(); @@ -61,8 +59,6 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count) msg.set_function(glesv2debugger::Message_Function_glDrawArrays); msg.set_type(glesv2debugger::Message_Type_AfterCall); msg.set_expect_response(expectResponse); - if (!expectResponse) - cmd.set_function(glesv2debugger::Message_Function_SKIP); if (!expectResponse) { cmd.set_function(glesv2debugger::Message_Function_SKIP); cmd.set_expect_response(false); @@ -154,10 +150,8 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* void * pixels = NULL; int viewport[4] = {}; - if (!expectResponse) { - cmd.set_function(glesv2debugger::Message_Function_CONTINUE); - cmd.set_expect_response(false); - } + cmd.set_function(glesv2debugger::Message_Function_CONTINUE); + cmd.set_expect_response(expectResponse); glesv2debugger::Message_Function oldCmd = cmd.function(); Send(msg, cmd); expectResponse = cmd.expect_response(); @@ -172,8 +166,6 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* msg.set_function(glesv2debugger::Message_Function_glDrawElements); msg.set_type(glesv2debugger::Message_Type_AfterCall); msg.set_expect_response(expectResponse); - if (!expectResponse) - cmd.set_function(glesv2debugger::Message_Function_SKIP); if (!expectResponse) { cmd.set_function(glesv2debugger::Message_Function_SKIP); cmd.set_expect_response(false); diff --git a/opengl/libs/GLES2_dbg/test/Android.mk b/opengl/libs/GLES2_dbg/test/Android.mk new file mode 100644 index 0000000000..14a84b447f --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/../src \ + $(LOCAL_PATH)/../../ \ + external/gtest/include \ + external/stlport/stlport \ + external/protobuf/src \ + bionic \ + external \ +# + +LOCAL_SRC_FILES:= \ + test_main.cpp \ + test_server.cpp \ + test_socket.cpp \ +# + +LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2_dbg libstlport +LOCAL_STATIC_LIBRARIES := libgtest libprotobuf-cpp-2.3.0-lite liblzf +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE:= libGLESv2_dbg_test + +ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER +endif +ifneq ($(TARGET_SIMULATOR),true) + LOCAL_C_INCLUDES += bionic/libc/private +endif + +LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\" +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_CFLAGS += -fvisibility=hidden + +include $(BUILD_EXECUTABLE) + diff --git a/opengl/libs/GLES2_dbg/test/test_main.cpp b/opengl/libs/GLES2_dbg/test/test_main.cpp new file mode 100644 index 0000000000..058bea4d6f --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/test_main.cpp @@ -0,0 +1,234 @@ +/* + ** Copyright 2011, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "header.h" +#include "gtest/gtest.h" +#include "hooks.h" + +namespace +{ + +// The fixture for testing class Foo. +class DbgContextTest : public ::testing::Test +{ +protected: + android::DbgContext dbg; + gl_hooks_t hooks; + + DbgContextTest() + : dbg(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE) { + // You can do set-up work for each test here. + hooks.gl.glGetError = GetError; + } + + static GLenum GetError() { + return GL_NO_ERROR; + } + + virtual ~DbgContextTest() { + // You can do clean-up work that doesn't throw exceptions here. + } + + // If the constructor and destructor are not enough for setting up + // and cleaning up each test, you can define the following methods: + + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test). + } + + virtual void TearDown() { + // Code here will be called immediately after each test (right + // before the destructor). + } +}; + +TEST_F(DbgContextTest, GetReadPixelBuffer) +{ + const unsigned int bufferSize = 512; + // test that it's allocating two buffers and swapping them + void * const buffer0 = dbg.GetReadPixelsBuffer(bufferSize); + ASSERT_NE((void *)NULL, buffer0); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) { + EXPECT_EQ(0, ((unsigned int *)buffer0)[i]) + << "GetReadPixelsBuffer should allocate and zero"; + ((unsigned int *)buffer0)[i] = i * 13; + } + + void * const buffer1 = dbg.GetReadPixelsBuffer(bufferSize); + ASSERT_NE((void *)NULL, buffer1); + EXPECT_NE(buffer0, buffer1); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) { + EXPECT_EQ(0, ((unsigned int *)buffer1)[i]) + << "GetReadPixelsBuffer should allocate and zero"; + ((unsigned int *)buffer1)[i] = i * 17; + } + + void * const buffer2 = dbg.GetReadPixelsBuffer(bufferSize); + EXPECT_EQ(buffer2, buffer0); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) + EXPECT_EQ(i * 13, ((unsigned int *)buffer2)[i]) + << "GetReadPixelsBuffer should swap buffers"; + + void * const buffer3 = dbg.GetReadPixelsBuffer(bufferSize); + EXPECT_EQ(buffer3, buffer1); + for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) + EXPECT_EQ(i * 17, ((unsigned int *)buffer3)[i]) + << "GetReadPixelsBuffer should swap buffers"; + + void * const buffer4 = dbg.GetReadPixelsBuffer(bufferSize); + EXPECT_NE(buffer3, buffer4); + EXPECT_EQ(buffer0, buffer2); + EXPECT_EQ(buffer1, buffer3); + EXPECT_EQ(buffer2, buffer4); + + // it reallocs as necessary; 0 size may result in NULL + for (unsigned int i = 0; i < 42; i++) { + void * const buffer = dbg.GetReadPixelsBuffer(((i & 7)) << 20); + EXPECT_NE((void *)NULL, buffer) + << "should be able to get a variety of reasonable sizes"; + EXPECT_TRUE(dbg.IsReadPixelBuffer(buffer)); + } +} + +TEST_F(DbgContextTest, CompressReadPixelBuffer) +{ + const unsigned int bufferSize = dbg.LZF_CHUNK_SIZE * 4 + 33; + std::string out; + unsigned char * buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + buffer[i] = i * 13; + dbg.CompressReadPixelBuffer(&out); + uint32_t decompSize = 0; + ASSERT_LT(12, out.length()); // at least written chunk header + ASSERT_EQ(bufferSize, *(uint32_t *)out.data()) + << "total decompressed size should be as requested in GetReadPixelsBuffer"; + for (unsigned int i = 4; i < out.length();) { + const uint32_t outSize = *(uint32_t *)(out.data() + i); + i += 4; + const uint32_t inSize = *(uint32_t *)(out.data() + i); + i += 4; + if (inSize == 0) + i += outSize; // chunk not compressed + else + i += inSize; // skip the actual compressed chunk + decompSize += outSize; + } + ASSERT_EQ(bufferSize, decompSize); + decompSize = 0; + + unsigned char * decomp = dbg.Decompress(out.data(), out.length(), &decompSize); + ASSERT_EQ(decompSize, bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + EXPECT_EQ((unsigned char)(i * 13), decomp[i]) << "xor with 0 ref is identity"; + free(decomp); + + buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + buffer[i] = i * 13; + out.clear(); + dbg.CompressReadPixelBuffer(&out); + decompSize = 0; + decomp = dbg.Decompress(out.data(), out.length(), &decompSize); + ASSERT_EQ(decompSize, bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + EXPECT_EQ(0, decomp[i]) << "xor with same ref is 0"; + free(decomp); + + buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + buffer[i] = i * 19; + out.clear(); + dbg.CompressReadPixelBuffer(&out); + decompSize = 0; + decomp = dbg.Decompress(out.data(), out.length(), &decompSize); + ASSERT_EQ(decompSize, bufferSize); + for (unsigned int i = 0; i < bufferSize; i++) + EXPECT_EQ((unsigned char)(i * 13) ^ (unsigned char)(i * 19), decomp[i]) + << "xor ref"; + free(decomp); +} + +TEST_F(DbgContextTest, UseProgram) +{ + static const GLuint _program = 74568; + static const struct Attribute { + const char * name; + GLint location; + GLint size; + GLenum type; + } _attributes [] = { + {"aaa", 2, 2, GL_FLOAT_VEC2}, + {"bb", 6, 2, GL_FLOAT_MAT2}, + {"c", 1, 1, GL_FLOAT}, + }; + static const unsigned int _attributeCount = sizeof(_attributes) / sizeof(*_attributes); + struct GL { + static void GetProgramiv(GLuint program, GLenum pname, GLint* params) { + EXPECT_EQ(_program, program); + ASSERT_NE((GLint *)NULL, params); + switch (pname) { + case GL_ACTIVE_ATTRIBUTES: + *params = _attributeCount; + return; + case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: + *params = 4; // includes NULL terminator + return; + default: + ADD_FAILURE() << "not handled pname: " << pname; + } + } + + static GLint GetAttribLocation(GLuint program, const GLchar* name) { + EXPECT_EQ(_program, program); + for (unsigned int i = 0; i < _attributeCount; i++) + if (!strcmp(name, _attributes[i].name)) + return _attributes[i].location; + ADD_FAILURE() << "unknown attribute name: " << name; + return -1; + } + + static void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, + GLsizei* length, GLint* size, GLenum* type, GLchar* name) { + EXPECT_EQ(_program, program); + ASSERT_LT(index, _attributeCount); + const Attribute & att = _attributes[index]; + ASSERT_GE(bufsize, strlen(att.name) + 1); + ASSERT_NE((GLint *)NULL, size); + ASSERT_NE((GLenum *)NULL, type); + ASSERT_NE((GLchar *)NULL, name); + strcpy(name, att.name); + if (length) + *length = strlen(name) + 1; + *size = att.size; + *type = att.type; + } + }; + hooks.gl.glGetProgramiv = GL::GetProgramiv; + hooks.gl.glGetAttribLocation = GL::GetAttribLocation; + hooks.gl.glGetActiveAttrib = GL::GetActiveAttrib; + dbg.glUseProgram(_program); + EXPECT_EQ(10, dbg.maxAttrib); + dbg.glUseProgram(0); + EXPECT_EQ(0, dbg.maxAttrib); +} +} // namespace + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/opengl/libs/GLES2_dbg/test/test_server.cpp b/opengl/libs/GLES2_dbg/test/test_server.cpp new file mode 100644 index 0000000000..7fb87ea11f --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/test_server.cpp @@ -0,0 +1,197 @@ +/* + ** Copyright 2011, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "header.h" +#include "gtest/gtest.h" +#include "egl_tls.h" +#include "hooks.h" + +namespace android +{ +extern FILE * file; +extern unsigned int MAX_FILE_SIZE; +extern pthread_key_t dbgEGLThreadLocalStorageKey; +}; + +// tmpfile fails, so need to manually make a writable file first +static const char * filePath = "/data/local/tmp/dump.gles2dbg"; + +class ServerFileTest : public ::testing::Test +{ +protected: + ServerFileTest() { } + + virtual ~ServerFileTest() { } + + virtual void SetUp() { + MAX_FILE_SIZE = 8 << 20; + ASSERT_EQ((FILE *)NULL, file); + file = fopen("/data/local/tmp/dump.gles2dbg", "wb+"); + ASSERT_NE((FILE *)NULL, file) << "make sure file is writable: " + << filePath; + } + + virtual void TearDown() { + ASSERT_NE((FILE *)NULL, file); + fclose(file); + file = NULL; + } + + void Read(glesv2debugger::Message & msg) const { + msg.Clear(); + uint32_t len = 0; + ASSERT_EQ(sizeof(len), fread(&len, 1, sizeof(len), file)); + ASSERT_GT(len, 0u); + char * buffer = new char [len]; + ASSERT_EQ(len, fread(buffer, 1, len, file)); + msg.ParseFromArray(buffer, len); + delete buffer; + } +}; + + + +TEST_F(ServerFileTest, Send) +{ + glesv2debugger::Message msg, cmd, read; + msg.set_context_id(1); + msg.set_function(msg.glFinish); + msg.set_expect_response(false); + msg.set_type(msg.BeforeCall); + rewind(file); + android::Send(msg, cmd); + rewind(file); + Read(read); + EXPECT_EQ(msg.context_id(), read.context_id()); + EXPECT_EQ(msg.function(), read.function()); + EXPECT_EQ(msg.expect_response(), read.expect_response()); + EXPECT_EQ(msg.type(), read.type()); +} + +void * glNoop() +{ + return 0; +} + +class ServerFileContextTest : public ServerFileTest +{ +protected: + tls_t tls; + gl_hooks_t hooks; + + ServerFileContextTest() { } + + virtual ~ServerFileContextTest() { } + + virtual void SetUp() { + ServerFileTest::SetUp(); + + if (dbgEGLThreadLocalStorageKey == -1) + pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL); + ASSERT_NE(-1, dbgEGLThreadLocalStorageKey); + tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE); + ASSERT_NE((void *)NULL, tls.dbg); + pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls); + for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++) + ((void **)&hooks)[i] = reinterpret_cast(glNoop); + } + + virtual void TearDown() { + ServerFileTest::TearDown(); + } +}; + +TEST_F(ServerFileContextTest, MessageLoop) +{ + static const int arg0 = 45; + static const float arg7 = -87.2331f; + static const int arg8 = -3; + static const int * ret = reinterpret_cast(870); + + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + msg.set_arg0(arg0); + msg.set_arg7((int &)arg7); + msg.set_arg8(arg8); + return ret; + } + } caller; + const int contextId = reinterpret_cast(tls.dbg); + glesv2debugger::Message msg, read; + + EXPECT_EQ(ret, MessageLoop(caller, msg, msg.glFinish)); + + rewind(file); + Read(read); + EXPECT_EQ(contextId, read.context_id()); + EXPECT_EQ(read.glFinish, read.function()); + EXPECT_EQ(false, read.expect_response()); + EXPECT_EQ(read.BeforeCall, read.type()); + + Read(read); + EXPECT_EQ(contextId, read.context_id()); + EXPECT_EQ(read.glFinish, read.function()); + EXPECT_EQ(false, read.expect_response()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_TRUE(read.has_time()); + EXPECT_EQ(arg0, read.arg0()); + const int readArg7 = read.arg7(); + EXPECT_EQ(arg7, (float &)readArg7); + EXPECT_EQ(arg8, read.arg8()); + + const long pos = ftell(file); + fseek(file, 0, SEEK_END); + EXPECT_EQ(pos, ftell(file)) + << "should only write the BeforeCall and AfterCall messages"; +} + +TEST_F(ServerFileContextTest, DisableEnableVertexAttribArray) +{ + Debug_glEnableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 2); // should just ignore invalid index + + glesv2debugger::Message read; + rewind(file); + Read(read); + EXPECT_EQ(read.glEnableVertexAttribArray, read.function()); + EXPECT_EQ(tls.dbg->MAX_VERTEX_ATTRIBS + 2, read.arg0()); + Read(read); + + rewind(file); + Debug_glDisableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 4); // should just ignore invalid index + rewind(file); + Read(read); + Read(read); + + for (unsigned int i = 0; i < tls.dbg->MAX_VERTEX_ATTRIBS; i += 5) { + rewind(file); + Debug_glEnableVertexAttribArray(i); + EXPECT_TRUE(tls.dbg->vertexAttribs[i].enabled); + rewind(file); + Read(read); + EXPECT_EQ(read.glEnableVertexAttribArray, read.function()); + EXPECT_EQ(i, read.arg0()); + Read(read); + + rewind(file); + Debug_glDisableVertexAttribArray(i); + EXPECT_FALSE(tls.dbg->vertexAttribs[i].enabled); + rewind(file); + Read(read); + EXPECT_EQ(read.glDisableVertexAttribArray, read.function()); + EXPECT_EQ(i, read.arg0()); + Read(read); + } +} diff --git a/opengl/libs/GLES2_dbg/test/test_socket.cpp b/opengl/libs/GLES2_dbg/test/test_socket.cpp new file mode 100644 index 0000000000..1c31a94a96 --- /dev/null +++ b/opengl/libs/GLES2_dbg/test/test_socket.cpp @@ -0,0 +1,474 @@ +/* + ** Copyright 2011, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include +#include + +#include "header.h" +#include "gtest/gtest.h" +#include "egl_tls.h" +#include "hooks.h" + +namespace android +{ +extern int serverSock, clientSock; +extern pthread_key_t dbgEGLThreadLocalStorageKey; +}; + +void * glNoop(); + +class SocketContextTest : public ::testing::Test +{ +protected: + tls_t tls; + gl_hooks_t hooks; + int sock; + char * buffer; + unsigned int bufferSize; + + SocketContextTest() : sock(-1) { + } + + virtual ~SocketContextTest() { + } + + virtual void SetUp() { + if (dbgEGLThreadLocalStorageKey == -1) + pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL); + ASSERT_NE(-1, dbgEGLThreadLocalStorageKey); + tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE); + ASSERT_NE((void *)NULL, tls.dbg); + pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls); + for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++) + ((void **)&hooks)[i] = (void *)glNoop; + + int socks[2] = {-1, -1}; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, socks)); + clientSock = socks[0]; + sock = socks[1]; + + bufferSize = 128; + buffer = new char [128]; + ASSERT_NE((char *)NULL, buffer); + } + + virtual void TearDown() { + close(sock); + close(clientSock); + clientSock = -1; + delete buffer; + } + + void Write(glesv2debugger::Message & msg) const { + msg.set_context_id((int)tls.dbg); + msg.set_type(msg.Response); + ASSERT_TRUE(msg.has_context_id()); + ASSERT_TRUE(msg.has_function()); + ASSERT_TRUE(msg.has_type()); + ASSERT_TRUE(msg.has_expect_response()); + static std::string str; + msg.SerializeToString(&str); + const uint32_t len = str.length(); + ASSERT_EQ(sizeof(len), send(sock, &len, sizeof(len), 0)); + ASSERT_EQ(str.length(), send(sock, str.data(), str.length(), 0)); + } + + void Read(glesv2debugger::Message & msg) { + int available = 0; + ASSERT_EQ(0, ioctl(sock, FIONREAD, &available)); + ASSERT_GT(available, 0); + uint32_t len = 0; + ASSERT_EQ(sizeof(len), recv(sock, &len, sizeof(len), 0)); + if (len > bufferSize) { + bufferSize = len; + buffer = new char[bufferSize]; + ASSERT_NE((char *)NULL, buffer); + } + ASSERT_EQ(len, recv(sock, buffer, len, 0)); + msg.Clear(); + msg.ParseFromArray(buffer, len); + ASSERT_TRUE(msg.has_context_id()); + ASSERT_TRUE(msg.has_function()); + ASSERT_TRUE(msg.has_type()); + ASSERT_TRUE(msg.has_expect_response()); + } + + void CheckNoAvailable() { + int available = 0; + ASSERT_EQ(0, ioctl(sock, FIONREAD, &available)); + ASSERT_EQ(available, 0); + } +}; + +TEST_F(SocketContextTest, MessageLoopSkip) +{ + static const int arg0 = 45; + static const float arg7 = -87.2331f; + static const int arg8 = -3; + static const int * ret = (int *)870; + + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + msg.set_arg0(arg0); + msg.set_arg7((int &)arg7); + msg.set_arg8(arg8); + return ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + tls.dbg->expectResponse.Bit(msg.glFinish, true); + + cmd.set_function(cmd.SKIP); + cmd.set_expect_response(false); + Write(cmd); + + EXPECT_NE(ret, MessageLoop(caller, msg, msg.glFinish)); + + Read(read); + EXPECT_EQ(read.glFinish, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_NE(arg0, read.arg0()); + EXPECT_NE((int &)arg7, read.arg7()); + EXPECT_NE(arg8, read.arg8()); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, MessageLoopContinue) +{ + static const int arg0 = GL_FRAGMENT_SHADER; + static const int ret = -342; + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + msg.set_ret(ret); + return (int *)ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + tls.dbg->expectResponse.Bit(msg.glCreateShader, true); + + cmd.set_function(cmd.CONTINUE); + cmd.set_expect_response(false); // MessageLoop should automatically skip after continue + Write(cmd); + + msg.set_arg0(arg0); + EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateShader)); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_EQ(arg0, read.arg0()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_EQ(ret, read.ret()); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, MessageLoopGenerateCall) +{ + static const int ret = -342; + static unsigned int createShader, createProgram; + createShader = 0; + createProgram = 0; + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + const int r = (int)_c->glCreateProgram(); + msg.set_ret(r); + return (int *)r; + } + static GLuint CreateShader(const GLenum type) { + createShader++; + return type; + } + static GLuint CreateProgram() { + createProgram++; + return ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glCreateShader = caller.CreateShader; + hooks.gl.glCreateProgram = caller.CreateProgram; + tls.dbg->expectResponse.Bit(msg.glCreateProgram, true); + + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_FRAGMENT_SHADER); + cmd.set_expect_response(true); + Write(cmd); + + cmd.Clear(); + cmd.set_function(cmd.CONTINUE); + cmd.set_expect_response(true); + Write(cmd); + + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_VERTEX_SHADER); + cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards + Write(cmd); + + EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram)); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_EQ(ret, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_VERTEX_SHADER, read.ret()); + + EXPECT_EQ(2, createShader); + EXPECT_EQ(1, createProgram); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, MessageLoopSetProp) +{ + static const int ret = -342; + static unsigned int createShader, createProgram; + createShader = 0; + createProgram = 0; + struct Caller : public FunctionCall { + const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) { + const int r = (int)_c->glCreateProgram(); + msg.set_ret(r); + return (int *)r; + } + static GLuint CreateShader(const GLenum type) { + createShader++; + return type; + } + static GLuint CreateProgram() { + createProgram++; + return ret; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glCreateShader = caller.CreateShader; + hooks.gl.glCreateProgram = caller.CreateProgram; + tls.dbg->expectResponse.Bit(msg.glCreateProgram, false); + + cmd.set_function(cmd.SETPROP); + cmd.set_prop(cmd.ExpectResponse); + cmd.set_arg0(cmd.glCreateProgram); + cmd.set_arg1(true); + cmd.set_expect_response(true); + Write(cmd); + + cmd.Clear(); + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_FRAGMENT_SHADER); + cmd.set_expect_response(true); + Write(cmd); + + cmd.set_function(cmd.SETPROP); + cmd.set_prop(cmd.CaptureDraw); + cmd.set_arg0(819); + cmd.set_expect_response(true); + Write(cmd); + + cmd.Clear(); + cmd.set_function(cmd.CONTINUE); + cmd.set_expect_response(true); + Write(cmd); + + cmd.set_function(cmd.glCreateShader); + cmd.set_arg0(GL_VERTEX_SHADER); + cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards + Write(cmd); + + EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram)); + + EXPECT_TRUE(tls.dbg->expectResponse.Bit(msg.glCreateProgram)); + EXPECT_EQ(819, tls.dbg->captureDraw); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateProgram, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + EXPECT_EQ(ret, read.ret()); + + Read(read); + EXPECT_EQ(read.glCreateShader, read.function()); + EXPECT_EQ(read.AfterGeneratedCall, read.type()); + EXPECT_EQ(GL_VERTEX_SHADER, read.ret()); + + EXPECT_EQ(2, createShader); + EXPECT_EQ(1, createProgram); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, TexImage2D) +{ + static const GLenum _target = GL_TEXTURE_2D; + static const GLint _level = 1, _internalformat = GL_RGBA; + static const GLsizei _width = 2, _height = 2; + static const GLint _border = 333; + static const GLenum _format = GL_RGB, _type = GL_UNSIGNED_SHORT_5_6_5; + static const short _pixels [_width * _height] = {11, 22, 33, 44}; + static unsigned int texImage2D; + texImage2D = 0; + + struct Caller { + static void TexImage2D(GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid* pixels) { + EXPECT_EQ(_target, target); + EXPECT_EQ(_level, level); + EXPECT_EQ(_internalformat, internalformat); + EXPECT_EQ(_width, width); + EXPECT_EQ(_height, height); + EXPECT_EQ(_border, border); + EXPECT_EQ(_format, format); + EXPECT_EQ(_type, type); + EXPECT_EQ(0, memcmp(_pixels, pixels, sizeof(_pixels))); + texImage2D++; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glTexImage2D = caller.TexImage2D; + tls.dbg->expectResponse.Bit(msg.glTexImage2D, false); + + Debug_glTexImage2D(_target, _level, _internalformat, _width, _height, _border, + _format, _type, _pixels); + EXPECT_EQ(1, texImage2D); + + Read(read); + EXPECT_EQ(read.glTexImage2D, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_EQ(_target, read.arg0()); + EXPECT_EQ(_level, read.arg1()); + EXPECT_EQ(_internalformat, read.arg2()); + EXPECT_EQ(_width, read.arg3()); + EXPECT_EQ(_height, read.arg4()); + EXPECT_EQ(_border, read.arg5()); + EXPECT_EQ(_format, read.arg6()); + EXPECT_EQ(_type, read.arg7()); + + EXPECT_TRUE(read.has_data()); + uint32_t dataLen = 0; + const unsigned char * data = tls.dbg->Decompress(read.data().data(), + read.data().length(), &dataLen); + EXPECT_EQ(sizeof(_pixels), dataLen); + if (sizeof(_pixels) == dataLen) + EXPECT_EQ(0, memcmp(_pixels, data, sizeof(_pixels))); + + Read(read); + EXPECT_EQ(read.glTexImage2D, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + + CheckNoAvailable(); +} + +TEST_F(SocketContextTest, CopyTexImage2D) +{ + static const GLenum _target = GL_TEXTURE_2D; + static const GLint _level = 1, _internalformat = GL_RGBA; + static const GLint _x = 9, _y = 99; + static const GLsizei _width = 2, _height = 3; + static const GLint _border = 333; + static const int _pixels [_width * _height] = {11, 22, 33, 44, 55, 66}; + static unsigned int copyTexImage2D, readPixels; + copyTexImage2D = 0, readPixels = 0; + + struct Caller { + static void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { + EXPECT_EQ(_target, target); + EXPECT_EQ(_level, level); + EXPECT_EQ(_internalformat, internalformat); + EXPECT_EQ(_x, x); + EXPECT_EQ(_y, y); + EXPECT_EQ(_width, width); + EXPECT_EQ(_height, height); + EXPECT_EQ(_border, border); + copyTexImage2D++; + } + static void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid* pixels) { + EXPECT_EQ(_x, x); + EXPECT_EQ(_y, y); + EXPECT_EQ(_width, width); + EXPECT_EQ(_height, height); + EXPECT_EQ(GL_RGBA, format); + EXPECT_EQ(GL_UNSIGNED_BYTE, type); + ASSERT_NE((void *)NULL, pixels); + memcpy(pixels, _pixels, sizeof(_pixels)); + readPixels++; + } + } caller; + glesv2debugger::Message msg, read, cmd; + hooks.gl.glCopyTexImage2D = caller.CopyTexImage2D; + hooks.gl.glReadPixels = caller.ReadPixels; + tls.dbg->expectResponse.Bit(msg.glCopyTexImage2D, false); + + Debug_glCopyTexImage2D(_target, _level, _internalformat, _x, _y, _width, _height, + _border); + ASSERT_EQ(1, copyTexImage2D); + ASSERT_EQ(1, readPixels); + + Read(read); + EXPECT_EQ(read.glCopyTexImage2D, read.function()); + EXPECT_EQ(read.BeforeCall, read.type()); + EXPECT_EQ(_target, read.arg0()); + EXPECT_EQ(_level, read.arg1()); + EXPECT_EQ(_internalformat, read.arg2()); + EXPECT_EQ(_x, read.arg3()); + EXPECT_EQ(_y, read.arg4()); + EXPECT_EQ(_width, read.arg5()); + EXPECT_EQ(_height, read.arg6()); + EXPECT_EQ(_border, read.arg7()); + + EXPECT_TRUE(read.has_data()); + EXPECT_EQ(read.ReferencedImage, read.data_type()); + EXPECT_EQ(GL_RGBA, read.pixel_format()); + EXPECT_EQ(GL_UNSIGNED_BYTE, read.pixel_type()); + uint32_t dataLen = 0; + unsigned char * const data = tls.dbg->Decompress(read.data().data(), + read.data().length(), &dataLen); + ASSERT_EQ(sizeof(_pixels), dataLen); + for (unsigned i = 0; i < sizeof(_pixels) / sizeof(*_pixels); i++) + EXPECT_EQ(_pixels[i], ((const int *)data)[i]) << "xor with 0 ref is identity"; + free(data); + + Read(read); + EXPECT_EQ(read.glCopyTexImage2D, read.function()); + EXPECT_EQ(read.AfterCall, read.type()); + + CheckNoAvailable(); +} -- 2.11.0