Debugger: Ensure GDB stub never hard-blocks

This commit is contained in:
Jeffrey Pfau 2015-01-19 02:34:36 -08:00
parent 29623ecd0b
commit 973f1a64a0
3 changed files with 95 additions and 31 deletions

View File

@ -12,6 +12,8 @@
#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 @@ static void _gdbStubPoll(struct ARMDebugger* debugger) {
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 _writeHostInfo(struct GDBStub* stub) {
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 @@ int GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddr
}
return 0;
}
if (!SocketSetBlocking(stub->socket, false)) {
goto cleanup;
}
int err = SocketListen(stub->socket, 1);
if (err) {
goto cleanup;
@ -496,14 +475,14 @@ cleanup:
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 @@ void GDBStubUpdate(struct GDBStub* stub) {
}
}
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;

View File

@ -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);

View File

@ -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