mirror of https://github.com/mgba-emu/mgba.git
Debugger: Ensure GDB stub never hard-blocks
This commit is contained in:
parent
29623ecd0b
commit
973f1a64a0
|
@ -12,6 +12,8 @@
|
||||||
#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */
|
#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SOCKET_TIMEOUT 50
|
||||||
|
|
||||||
enum GDBError {
|
enum GDBError {
|
||||||
GDB_NO_ERROR = 0x00,
|
GDB_NO_ERROR = 0x00,
|
||||||
GDB_BAD_ARGUMENTS = 0x06,
|
GDB_BAD_ARGUMENTS = 0x06,
|
||||||
|
@ -58,33 +60,13 @@ static void _gdbStubPoll(struct ARMDebugger* debugger) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||||
if (stub->shouldBlock) {
|
|
||||||
stub->shouldBlock = false;
|
stub->shouldBlock = false;
|
||||||
if (!SocketSetBlocking(stub->socket, false)) {
|
|
||||||
GDBStubHangup(stub);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!SOCKET_FAILED(stub->connection) && !SocketSetBlocking(stub->connection, false)) {
|
|
||||||
GDBStubHangup(stub);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GDBStubUpdate(stub);
|
GDBStubUpdate(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gdbStubWait(struct ARMDebugger* debugger) {
|
static void _gdbStubWait(struct ARMDebugger* debugger) {
|
||||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||||
if (!stub->shouldBlock) {
|
|
||||||
stub->shouldBlock = true;
|
stub->shouldBlock = true;
|
||||||
if (!SocketSetBlocking(stub->socket, true)) {
|
|
||||||
GDBStubHangup(stub);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!SOCKET_FAILED(stub->connection) && !SocketSetBlocking(stub->connection, true)) {
|
|
||||||
GDBStubHangup(stub);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GDBStubUpdate(stub);
|
GDBStubUpdate(stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,12 +177,6 @@ static void _writeHostInfo(struct GDBStub* stub) {
|
||||||
static void _continue(struct GDBStub* stub, const char* message) {
|
static void _continue(struct GDBStub* stub, const char* message) {
|
||||||
stub->d.state = DEBUGGER_CUSTOM;
|
stub->d.state = DEBUGGER_CUSTOM;
|
||||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||||
if (!SOCKET_FAILED(stub->connection)) {
|
|
||||||
if (!SocketSetBlocking(stub->connection, 0)) {
|
|
||||||
GDBStubHangup(stub);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: parse message
|
// TODO: parse message
|
||||||
UNUSED(message);
|
UNUSED(message);
|
||||||
}
|
}
|
||||||
|
@ -484,6 +460,9 @@ int GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddr
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (!SocketSetBlocking(stub->socket, false)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
int err = SocketListen(stub->socket, 1);
|
int err = SocketListen(stub->socket, 1);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
@ -496,14 +475,14 @@ cleanup:
|
||||||
stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
|
stub->d.log(&stub->d, DEBUGGER_LOG_ERROR, "Couldn't listen on port");
|
||||||
}
|
}
|
||||||
SocketClose(stub->socket);
|
SocketClose(stub->socket);
|
||||||
stub->socket = -1;
|
stub->socket = INVALID_SOCKET;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDBStubHangup(struct GDBStub* stub) {
|
void GDBStubHangup(struct GDBStub* stub) {
|
||||||
if (!SOCKET_FAILED(stub->connection)) {
|
if (!SOCKET_FAILED(stub->connection)) {
|
||||||
SocketClose(stub->connection);
|
SocketClose(stub->connection);
|
||||||
stub->connection = -1;
|
stub->connection = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
if (stub->d.state == DEBUGGER_PAUSED) {
|
if (stub->d.state == DEBUGGER_PAUSED) {
|
||||||
stub->d.state = DEBUGGER_RUNNING;
|
stub->d.state = DEBUGGER_RUNNING;
|
||||||
|
@ -514,17 +493,27 @@ void GDBStubShutdown(struct GDBStub* stub) {
|
||||||
GDBStubHangup(stub);
|
GDBStubHangup(stub);
|
||||||
if (!SOCKET_FAILED(stub->socket)) {
|
if (!SOCKET_FAILED(stub->socket)) {
|
||||||
SocketClose(stub->socket);
|
SocketClose(stub->socket);
|
||||||
stub->socket = -1;
|
stub->socket = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDBStubUpdate(struct GDBStub* stub) {
|
void GDBStubUpdate(struct GDBStub* stub) {
|
||||||
if (stub->socket == INVALID_SOCKET) {
|
if (stub->socket == INVALID_SOCKET) {
|
||||||
|
if (stub->d.state == DEBUGGER_PAUSED) {
|
||||||
|
stub->d.state = DEBUGGER_RUNNING;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stub->connection == INVALID_SOCKET) {
|
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);
|
stub->connection = SocketAccept(stub->socket, 0);
|
||||||
if (!SOCKET_FAILED(stub->connection)) {
|
if (!SOCKET_FAILED(stub->connection)) {
|
||||||
|
if (!SocketSetBlocking(stub->connection, false)) {
|
||||||
|
goto connectionLost;
|
||||||
|
}
|
||||||
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
|
ARMDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED);
|
||||||
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
} else if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||||
return;
|
return;
|
||||||
|
@ -533,6 +522,10 @@ void GDBStubUpdate(struct GDBStub* stub) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (true) {
|
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);
|
ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
|
||||||
if (messageLen == 0) {
|
if (messageLen == 0) {
|
||||||
goto connectionLost;
|
goto connectionLost;
|
||||||
|
|
|
@ -47,6 +47,8 @@ void GDBController::detach() {
|
||||||
if (!isAttached()) {
|
if (!isAttached()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
SocketSetBlocking(m_gdbStub.socket, false);
|
||||||
|
SocketSetBlocking(m_gdbStub.connection, false);
|
||||||
m_gameController->threadInterrupt();
|
m_gameController->threadInterrupt();
|
||||||
GDBStubShutdown(&m_gdbStub);
|
GDBStubShutdown(&m_gdbStub);
|
||||||
m_gameController->setDebugger(nullptr);
|
m_gameController->setDebugger(nullptr);
|
||||||
|
|
|
@ -179,4 +179,73 @@ static inline int SocketSetTCPPush(Socket socket, int push) {
|
||||||
return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue