/* ** 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 #include #include #include #include #include "header.h" namespace android { int serverSock = -1, clientSock = -1; FILE * file = NULL; unsigned int MAX_FILE_SIZE = 0; int timeMode = SYSTEM_TIME_THREAD; static void Die(const char * msg) { LOGD("\n*\n*\n* GLESv2_dbg: Die: %s \n*\n*", msg); StopDebugServer(); exit(1); } void StartDebugServer(const unsigned short port, const bool forceUseFile, const unsigned int maxFileSize, const char * const filePath) { MAX_FILE_SIZE = maxFileSize; LOGD("GLESv2_dbg: StartDebugServer"); if (serverSock >= 0 || file) return; LOGD("GLESv2_dbg: StartDebugServer create socket"); struct sockaddr_in server = {}, client = {}; /* Create the TCP socket */ if (forceUseFile || (serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { file = fopen(filePath, "wb"); if (!file) Die("Failed to create socket and file"); else return; } /* Construct the server sockaddr_in structure */ server.sin_family = AF_INET; /* Internet/IP */ server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Incoming addr */ server.sin_port = htons(port); /* server port */ /* Bind the server socket */ socklen_t sizeofSockaddr_in = sizeof(sockaddr_in); if (bind(serverSock, (struct sockaddr *) &server, sizeof(server)) < 0) { Die("Failed to bind the server socket"); } /* Listen on the server socket */ if (listen(serverSock, 1) < 0) { Die("Failed to listen on server socket"); } LOGD("server started on %d \n", server.sin_port); /* Wait for client connection */ if ((clientSock = accept(serverSock, (struct sockaddr *) &client, &sizeofSockaddr_in)) < 0) { Die("Failed to accept client connection"); } LOGD("Client connected: %s\n", inet_ntoa(client.sin_addr)); // fcntl(clientSock, F_SETFL, O_NONBLOCK); } void StopDebugServer() { LOGD("GLESv2_dbg: StopDebugServer"); if (clientSock > 0) { close(clientSock); clientSock = -1; } if (serverSock > 0) { close(serverSock); serverSock = -1; } if (file) { fclose(file); file = NULL; } } void Receive(glesv2debugger::Message & cmd) { if (clientSock < 0) return; unsigned len = 0; int received = recv(clientSock, &len, 4, MSG_WAITALL); if (received < 0) Die("Failed to receive response length"); else if (4 != received) { LOGD("received %dB: %.8X", received, len); Die("Received length mismatch, expected 4"); } static void * buffer = NULL; static unsigned bufferSize = 0; if (bufferSize < len) { buffer = realloc(buffer, len); assert(buffer); bufferSize = len; } received = recv(clientSock, buffer, len, MSG_WAITALL); if (received < 0) Die("Failed to receive response"); else if (len != received) Die("Received length mismatch"); cmd.Clear(); cmd.ParseFromArray(buffer, len); } bool TryReceive(glesv2debugger::Message & cmd) { if (clientSock < 0) return false; fd_set readSet; FD_ZERO(&readSet); FD_SET(clientSock, &readSet); timeval timeout; timeout.tv_sec = timeout.tv_usec = 0; int rc = select(clientSock + 1, &readSet, NULL, NULL, &timeout); if (rc < 0) Die("failed to select clientSock"); bool received = false; if (FD_ISSET(clientSock, &readSet)) { LOGD("TryReceive: avaiable for read"); Receive(cmd); return true; } return false; } float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd) { // TODO: use per DbgContext send/receive buffer and async socket // instead of mutex and blocking io; watch out for large messages static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; struct Autolock { Autolock() { pthread_mutex_lock(&mutex); } ~Autolock() { pthread_mutex_unlock(&mutex); } } autolock; if (msg.function() != glesv2debugger::Message_Function_ACK) assert(msg.has_context_id() && msg.context_id() != 0); static std::string str; msg.SerializeToString(&str); const uint32_t len = str.length(); if (clientSock < 0) { if (file) { fwrite(&len, sizeof(len), 1, file); fwrite(str.data(), len, 1, file); if (ftell(file) >= MAX_FILE_SIZE) { fclose(file); Die("MAX_FILE_SIZE reached"); } } return 0; } int sent = -1; sent = send(clientSock, &len, sizeof(len), 0); if (sent != sizeof(len)) { LOGD("actual sent=%d expected=%d clientSock=%d", sent, sizeof(len), clientSock); Die("Failed to send message length"); } nsecs_t c0 = systemTime(timeMode); 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); Die("Failed to send message"); } // TODO: factor Receive & TryReceive out and into MessageLoop, or add control argument. // mean while, if server is sending a SETPROP then don't try to receive, // because server will not be processing received command if (msg.function() == msg.SETPROP) return t; // try to receive commands even though not expecting response, // since client can send SETPROP and other commands anytime if (!msg.expect_response()) { if (TryReceive(cmd)) { if (glesv2debugger::Message_Function_SETPROP == cmd.function()) LOGD("Send: TryReceived SETPROP"); else LOGD("Send: TryReceived %u", cmd.function()); } } else Receive(cmd); return t; } void SetProp(DbgContext * const dbg, const glesv2debugger::Message & cmd) { switch (cmd.prop()) { case glesv2debugger::Message_Prop_CaptureDraw: LOGD("SetProp Message_Prop_CaptureDraw %d", cmd.arg0()); dbg->captureDraw = cmd.arg0(); break; case glesv2debugger::Message_Prop_TimeMode: LOGD("SetProp Message_Prop_TimeMode %d", cmd.arg0()); timeMode = cmd.arg0(); break; case glesv2debugger::Message_Prop_ExpectResponse: LOGD("SetProp Message_Prop_ExpectResponse %d=%d", cmd.arg0(), cmd.arg1()); dbg->expectResponse.Bit((glesv2debugger::Message_Function)cmd.arg0(), cmd.arg1()); break; case glesv2debugger::Message_Prop_CaptureSwap: LOGD("SetProp CaptureSwap %d", cmd.arg0()); dbg->captureSwap = cmd.arg0(); break; default: assert(0); } } int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg, const glesv2debugger::Message_Function function) { DbgContext * const dbg = getDbgContextThreadSpecific(); const int * ret = 0; glesv2debugger::Message cmd; msg.set_context_id(reinterpret_cast(dbg)); msg.set_type(glesv2debugger::Message_Type_BeforeCall); bool expectResponse = dbg->expectResponse.Bit(function); msg.set_expect_response(expectResponse); 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(expectResponse); glesv2debugger::Message_Function oldCmd = cmd.function(); Send(msg, cmd); expectResponse = cmd.expect_response(); while (true) { msg.Clear(); nsecs_t c0 = systemTime(timeMode); switch (cmd.function()) { case glesv2debugger::Message_Function_CONTINUE: ret = functionCall(&dbg->hooks->gl, msg); while (GLenum error = dbg->hooks->gl.glGetError()) LOGD("Function=%u glGetError() = 0x%.4X", function, error); if (!msg.has_time()) // some has output data copy, so time inside call msg.set_time((systemTime(timeMode) - c0) * 1e-6f); msg.set_context_id(reinterpret_cast(dbg)); msg.set_function(function); msg.set_type(glesv2debugger::Message_Type_AfterCall); msg.set_expect_response(expectResponse); if (!expectResponse) { cmd.set_function(glesv2debugger::Message_Function_SKIP); cmd.set_expect_response(false); } oldCmd = cmd.function(); Send(msg, cmd); expectResponse = cmd.expect_response(); break; case glesv2debugger::Message_Function_SKIP: return const_cast(ret); case glesv2debugger::Message_Function_SETPROP: SetProp(dbg, cmd); expectResponse = cmd.expect_response(); if (!expectResponse) // SETPROP is "out of band" cmd.set_function(oldCmd); else Receive(cmd); break; default: ret = GenerateCall(dbg, cmd, msg, ret); msg.set_expect_response(expectResponse); if (!expectResponse) { cmd.set_function(cmd.SKIP); cmd.set_expect_response(expectResponse); } oldCmd = cmd.function(); Send(msg, cmd); expectResponse = cmd.expect_response(); break; } } return 0; } }; // namespace android {