Debugger: Ensure GDB stub never hard-blocks
Jeffrey Pfau jeffrey@endrift.com
Mon, 19 Jan 2015 02:34:36 -0800
3 files changed,
95 insertions(+),
31 deletions(-)
M
src/debugger/gdb-stub.c
→
src/debugger/gdb-stub.c
@@ -12,6 +12,8 @@ #ifndef SIGTRAP
#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */ #endif +#define SOCKET_TIMEOUT 50 + enum GDBError { GDB_NO_ERROR = 0x00, GDB_BAD_ARGUMENTS = 0x06,@@ -58,33 +60,13 @@ if (stub->untilPoll > 0) {
return; } stub->untilPoll = GDB_STUB_INTERVAL; - if (stub->shouldBlock) { - stub->shouldBlock = false; - if (!SocketSetBlocking(stub->socket, false)) { - GDBStubHangup(stub); - return; - } - if (!SOCKET_FAILED(stub->connection) && !SocketSetBlocking(stub->connection, false)) { - GDBStubHangup(stub); - return; - } - } + stub->shouldBlock = false; GDBStubUpdate(stub); } static void _gdbStubWait(struct ARMDebugger* debugger) { struct GDBStub* stub = (struct GDBStub*) debugger; - if (!stub->shouldBlock) { - stub->shouldBlock = true; - if (!SocketSetBlocking(stub->socket, true)) { - GDBStubHangup(stub); - return; - } - if (!SOCKET_FAILED(stub->connection) && !SocketSetBlocking(stub->connection, true)) { - GDBStubHangup(stub); - return; - } - } + stub->shouldBlock = true; GDBStubUpdate(stub); }@@ -195,12 +177,6 @@
static void _continue(struct GDBStub* stub, const char* message) { stub->d.state = DEBUGGER_CUSTOM; stub->untilPoll = GDB_STUB_INTERVAL; - if (!SOCKET_FAILED(stub->connection)) { - if (!SocketSetBlocking(stub->connection, 0)) { - GDBStubHangup(stub); - return; - } - } // TODO: parse message UNUSED(message); }@@ -484,6 +460,9 @@ stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't open socket");
} return 0; } + if (!SocketSetBlocking(stub->socket, false)) { + goto cleanup; + } int err = SocketListen(stub->socket, 1); if (err) { goto cleanup;@@ -496,14 +475,14 @@ if (stub->d.log) {
stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port"); } SocketClose(stub->socket); - stub->socket = -1; + stub->socket = INVALID_SOCKET; return 0; } void GDBStubHangup(struct GDBStub* stub) { if (!SOCKET_FAILED(stub->connection)) { SocketClose(stub->connection); - stub->connection = -1; + stub->connection = INVALID_SOCKET; } if (stub->d.state == DEBUGGER_PAUSED) { stub->d.state = DEBUGGER_RUNNING;@@ -514,17 +493,27 @@ void GDBStubShutdown(struct GDBStub* stub) {
GDBStubHangup(stub); if (!SOCKET_FAILED(stub->socket)) { SocketClose(stub->socket); - stub->socket = -1; + stub->socket = INVALID_SOCKET; } } void GDBStubUpdate(struct GDBStub* stub) { if (stub->socket == INVALID_SOCKET) { + if (stub->d.state == DEBUGGER_PAUSED) { + stub->d.state = DEBUGGER_RUNNING; + } return; } if (stub->connection == INVALID_SOCKET) { + if (stub->shouldBlock) { + Socket reads = stub->socket; + SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT); + } stub->connection = SocketAccept(stub->socket, 0); if (!SOCKET_FAILED(stub->connection)) { + if (!SocketSetBlocking(stub->connection, false)) { + goto connectionLost; + } ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED); } else if (errno == EWOULDBLOCK || errno == EAGAIN) { return;@@ -533,6 +522,10 @@ goto connectionLost;
} } while (true) { + if (stub->shouldBlock) { + Socket reads = stub->connection; + SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT); + } ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1); if (messageLen == 0) { goto connectionLost;
M
src/platform/qt/GDBController.cpp
→
src/platform/qt/GDBController.cpp
@@ -47,6 +47,8 @@ void GDBController::detach() {
if (!isAttached()) { return; } + SocketSetBlocking(m_gdbStub.socket, false); + SocketSetBlocking(m_gdbStub.connection, false); m_gameController->threadInterrupt(); GDBStubShutdown(&m_gdbStub); m_gameController->setDebugger(nullptr);
M
src/util/socket.h
→
src/util/socket.h
@@ -179,4 +179,73 @@ static inline int SocketSetTCPPush(Socket socket, int push) {
return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0; } +static inline int SocketPoll(int nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) { + fd_set rset; + fd_set wset; + fd_set eset; + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_ZERO(&eset); + int i; + int maxFd = 0; + if (reads) { + for (i = 0; i < nSockets; ++i) { + if (SOCKET_FAILED(reads[i])) { + break; + } + if (reads[i] > maxFd) { + maxFd = reads[i]; + } + FD_SET(reads[i], &rset); + reads[i] = INVALID_SOCKET; + } + } + if (writes) { + for (i = 0; i < nSockets; ++i) { + if (SOCKET_FAILED(writes[i])) { + break; + } + if (writes[i] > maxFd) { + maxFd = writes[i]; + } + FD_SET(writes[i], &wset); + writes[i] = INVALID_SOCKET; + } + } + if (errors) { + for (i = 0; i < nSockets; ++i) { + if (SOCKET_FAILED(errors[i])) { + break; + } + if (errors[i] > maxFd) { + maxFd = errors[i]; + } + FD_SET(errors[i], &eset); + errors[i] = INVALID_SOCKET; + } + } + struct timeval tv; + tv.tv_sec = timeoutMillis / 1000; + tv.tv_usec = (timeoutMillis % 1000) * 1000; + int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv); + int r = 0; + int w = 0; + int e = 0; + for (i = 0; i < maxFd; ++i) { + if (reads && FD_ISSET(i, &rset)) { + reads[r] = i; + ++r; + } + if (writes && FD_ISSET(i, &wset)) { + writes[w] = i; + ++w; + } + if (errors && FD_ISSET(i, &eset)) { + errors[e] = i; + ++e; + } + } + return result; +} + #endif