diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index 87f80197b..122fdde6f 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -152,7 +152,7 @@ struct mDebuggerModule { void (*init)(struct mDebuggerModule*); void (*deinit)(struct mDebuggerModule*); - void (*paused)(struct mDebuggerModule*); + void (*paused)(struct mDebuggerModule*, int32_t timeoutMs); void (*update)(struct mDebuggerModule*); void (*entered)(struct mDebuggerModule*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); void (*custom)(struct mDebuggerModule*); @@ -166,6 +166,7 @@ void mDebuggerDeinit(struct mDebugger*); void mDebuggerAttach(struct mDebugger*, struct mCore*); void mDebuggerAttachModule(struct mDebugger*, struct mDebuggerModule*); void mDebuggerDetachModule(struct mDebugger*, struct mDebuggerModule*); +void mDebuggerRunTimeout(struct mDebugger* debugger, int32_t timeoutMs); void mDebuggerRun(struct mDebugger*); void mDebuggerRunFrame(struct mDebugger*); void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); diff --git a/include/mgba/internal/debugger/cli-debugger.h b/include/mgba/internal/debugger/cli-debugger.h index f87bffd7e..87ba9db88 100644 --- a/include/mgba/internal/debugger/cli-debugger.h +++ b/include/mgba/internal/debugger/cli-debugger.h @@ -73,6 +73,7 @@ struct CLIDebuggerBackend { ATTRIBUTE_FORMAT(printf, 2, 3) void (*printf)(struct CLIDebuggerBackend*, const char* fmt, ...); + int (*poll)(struct CLIDebuggerBackend*, int32_t timeoutMs); const char* (*readline)(struct CLIDebuggerBackend*, size_t* len); void (*lineAppend)(struct CLIDebuggerBackend*, const char* line); const char* (*historyLast)(struct CLIDebuggerBackend*, size_t* len); diff --git a/include/mgba/internal/debugger/gdb-stub.h b/include/mgba/internal/debugger/gdb-stub.h index 0d6d24b29..6110ec499 100644 --- a/include/mgba/internal/debugger/gdb-stub.h +++ b/include/mgba/internal/debugger/gdb-stub.h @@ -41,7 +41,6 @@ struct GDBStub { Socket socket; Socket connection; - bool shouldBlock; int untilPoll; bool supportsSwbreak; @@ -56,7 +55,7 @@ bool GDBStubListen(struct GDBStub*, int port, const struct Address* bindAddress, void GDBStubHangup(struct GDBStub*); void GDBStubShutdown(struct GDBStub*); -void GDBStubUpdate(struct GDBStub*); +bool GDBStubUpdate(struct GDBStub*, int timeoutMs); CXX_GUARD_END diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 79313d06c..58ce72a25 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -1092,7 +1092,7 @@ bool CLIDebuggerRunCommand(struct CLIDebugger* debugger, const char* line, size_ return false; } -static void _commandLine(struct mDebuggerModule* debugger) { +static void _commandLine(struct mDebuggerModule* debugger, int32_t timeoutMs) { struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger; const char* line; size_t len; @@ -1102,15 +1102,20 @@ static void _commandLine(struct mDebuggerModule* debugger) { _printStatus(cliDebugger, 0); } while (debugger->isPaused && !mDebuggerIsShutdown(debugger->p)) { + int poll = cliDebugger->backend->poll(cliDebugger->backend, timeoutMs); + if (poll <= 0) { + if (poll < 0) { + mDebuggerShutdown(debugger->p); + } else { + cliDebugger->skipStatus = true; + } + return; + } line = cliDebugger->backend->readline(cliDebugger->backend, &len); if (!line || len == 0) { mDebuggerShutdown(debugger->p); return; } - if (line[0] == '\033') { - cliDebugger->skipStatus = true; - return; - } if (line[0] == '\n') { line = cliDebugger->backend->historyLast(cliDebugger->backend, &len); if (line && len) { diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index d3819a754..cc0b32683 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -118,7 +118,7 @@ void mDebuggerDetachModule(struct mDebugger* debugger, struct mDebuggerModule* m } } -void mDebuggerRun(struct mDebugger* debugger) { +void mDebuggerRunTimeout(struct mDebugger* debugger, int32_t timeoutMs) { size_t i; size_t anyPaused = 0; @@ -146,7 +146,7 @@ void mDebuggerRun(struct mDebugger* debugger) { struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i); if (module->isPaused) { if (module->paused) { - module->paused(module); + module->paused(module, timeoutMs); } if (module->isPaused) { ++anyPaused; @@ -167,6 +167,10 @@ void mDebuggerRun(struct mDebugger* debugger) { } } +void mDebuggerRun(struct mDebugger* debugger) { + mDebuggerRunTimeout(debugger, 50); +} + void mDebuggerRunFrame(struct mDebugger* debugger) { uint32_t frame = debugger->core->frameCounter(debugger->core); do { @@ -309,7 +313,5 @@ bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int void mDebuggerModuleSetNeedsCallback(struct mDebuggerModule* debugger) { debugger->needsCallback = true; - if (debugger->p->state == DEBUGGER_RUNNING) { - debugger->p->state = DEBUGGER_CALLBACK; - } + mDebuggerUpdatePaused(debugger->p); } diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 88bd1e6a5..8a7d400ee 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -17,8 +17,6 @@ #define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */ #endif -#define SOCKET_TIMEOUT 50 - enum GDBError { GDB_NO_ERROR = 0x00, GDB_BAD_ARGUMENTS = 0x06, @@ -135,20 +133,17 @@ static void _gdbStubPoll(struct mDebuggerModule* debugger) { return; } stub->untilPoll = GDB_STUB_INTERVAL; - stub->shouldBlock = false; - GDBStubUpdate(stub); + GDBStubUpdate(stub, 0); } -static void _gdbStubWait(struct mDebuggerModule* debugger) { +static void _gdbStubWait(struct mDebuggerModule* debugger, int32_t timeoutMs) { struct GDBStub* stub = (struct GDBStub*) debugger; - stub->shouldBlock = true; - GDBStubUpdate(stub); + GDBStubUpdate(stub, timeoutMs); } static void _gdbStubUpdate(struct mDebuggerModule* debugger) { struct GDBStub* stub = (struct GDBStub*) debugger; - stub->shouldBlock = false; - GDBStubUpdate(stub); + GDBStubUpdate(stub, 0); } static void _ack(struct GDBStub* stub) { @@ -254,6 +249,7 @@ static void _writeHostInfo(struct GDBStub* stub) { static void _continue(struct GDBStub* stub, const char* message) { mDebuggerModuleSetNeedsCallback(&stub->d); stub->untilPoll = GDB_STUB_INTERVAL; + stub->d.isPaused = false; // TODO: parse message UNUSED(message); } @@ -791,7 +787,6 @@ void GDBStubCreate(struct GDBStub* stub) { stub->d.type = DEBUGGER_GDB; stub->untilPoll = GDB_STUB_INTERVAL; stub->lineAck = GDB_ACK_PENDING; - stub->shouldBlock = false; } bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress, enum GDBWatchpointsBehvaior watchpointsBehavior) { @@ -841,17 +836,17 @@ void GDBStubShutdown(struct GDBStub* stub) { } } -void GDBStubUpdate(struct GDBStub* stub) { +bool GDBStubUpdate(struct GDBStub* stub, int32_t timeoutMs) { if (stub->socket == INVALID_SOCKET) { stub->d.needsCallback = false; stub->d.isPaused = false; mDebuggerUpdatePaused(stub->d.p); - return; + return false; } if (stub->connection == INVALID_SOCKET) { - if (stub->shouldBlock) { + if (timeoutMs) { Socket reads = stub->socket; - SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT); + SocketPoll(1, &reads, 0, 0, timeoutMs); } stub->connection = SocketAccept(stub->socket, 0); if (!SOCKET_FAILED(stub->connection)) { @@ -860,36 +855,38 @@ void GDBStubUpdate(struct GDBStub* stub) { } mDebuggerEnter(stub->d.p, DEBUGGER_ENTER_ATTACHED, 0); } else if (SocketWouldBlock()) { - return; + return false; } else { goto connectionLost; } SocketSetTCPPush(stub->connection, 1); } - 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; - } - if (messageLen == -1) { - if (SocketWouldBlock()) { - return; - } - goto connectionLost; - } - stub->line[messageLen] = '\0'; - mLOG(DEBUGGER, DEBUG, "< %s", stub->line); - ssize_t position = 0; - while (position < messageLen) { - position += _parseGDBMessage(stub, &stub->line[position]); - } + + if (timeoutMs) { + Socket reads = stub->connection; + SocketPoll(1, &reads, 0, 0, timeoutMs); } + ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1); + if (messageLen == 0) { + goto connectionLost; + } + if (messageLen == -1) { + if (SocketWouldBlock()) { + return false; + } + goto connectionLost; + } + + stub->line[messageLen] = '\0'; + mLOG(DEBUGGER, DEBUG, "< %s", stub->line); + ssize_t position = 0; + while (position < messageLen) { + position += _parseGDBMessage(stub, &stub->line[position]); + } + return true; connectionLost: mLOG(DEBUGGER, WARN, "Connection lost"); GDBStubHangup(stub); + return false; } diff --git a/src/feature/editline/cli-el-backend.c b/src/feature/editline/cli-el-backend.c index b87ecb6bf..78ae19fe1 100644 --- a/src/feature/editline/cli-el-backend.c +++ b/src/feature/editline/cli-el-backend.c @@ -7,12 +7,31 @@ #include #include +#include #include #include +struct CLIDebuggerEditLineBackend { + struct CLIDebuggerBackend d; + + EditLine* elstate; + History* histate; + + int count; + const char* prompt; + bool doPrompt; + Thread promptThread; + Mutex promptMutex; + Condition promptRead; + Condition promptWrite; + bool exitThread; +}; + static struct CLIDebugger* _activeDebugger; +static THREAD_ENTRY _promptThread(void*); + static char* _prompt(EditLine* el) { UNUSED(el); return "> "; @@ -43,7 +62,7 @@ static unsigned char _tabComplete(EditLine* elstate, int ch) { } ATTRIBUTE_FORMAT(printf, 2, 3) -void _CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt, ...) { +static void CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt, ...) { UNUSED(be); va_list args; va_start(args, fmt); @@ -51,7 +70,7 @@ void _CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt, va_end(args); } -void _CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) { +static void CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) { struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; // TODO: get argv[0] elbe->elstate = el_init(binaryName, stdin, stdout, stderr); @@ -81,12 +100,26 @@ void _CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) { } } + MutexInit(&elbe->promptMutex); + ConditionInit(&elbe->promptRead); + ConditionInit(&elbe->promptWrite); + elbe->prompt = NULL; + elbe->exitThread = false; + elbe->doPrompt = false; + ThreadCreate(&elbe->promptThread, _promptThread, elbe); + _activeDebugger = be->p; signal(SIGINT, _breakIntoDefault); } -void _CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) { +static void CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) { struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; + MutexLock(&elbe->promptMutex); + elbe->exitThread = true; + ConditionWake(&elbe->promptWrite); + MutexUnlock(&elbe->promptMutex); + ThreadJoin(&elbe->promptThread); + char path[PATH_MAX + 1]; mCoreConfigDirectory(path, PATH_MAX); if (path[0]) { @@ -111,11 +144,51 @@ void _CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) { free(elbe); } -const char* _CLIDebuggerEditLineReadLine(struct CLIDebuggerBackend* be, size_t* len) { +static THREAD_ENTRY _promptThread(void* context) { + struct CLIDebuggerEditLineBackend* elbe = context; + + MutexLock(&elbe->promptMutex); + while (!elbe->exitThread) { + if (elbe->doPrompt) { + MutexUnlock(&elbe->promptMutex); + elbe->prompt = el_gets(elbe->elstate, &elbe->count); + MutexLock(&elbe->promptMutex); + elbe->doPrompt = false; + ConditionWake(&elbe->promptRead); + } + ConditionWait(&elbe->promptWrite, &elbe->promptMutex); + } + MutexUnlock(&elbe->promptMutex); +} + +static int CLIDebuggerEditLinePoll(struct CLIDebuggerBackend* be, int32_t timeoutMs) { + struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; + int gotPrompt = 0; + MutexLock(&elbe->promptMutex); + if (!elbe->prompt) { + elbe->doPrompt = true; + ConditionWake(&elbe->promptWrite); + ConditionWaitTimed(&elbe->promptRead, &elbe->promptMutex, timeoutMs); + } + if (elbe->prompt) { + gotPrompt = 1; + } + MutexUnlock(&elbe->promptMutex); + + return gotPrompt; +} + +static const char* CLIDebuggerEditLineReadLine(struct CLIDebuggerBackend* be, size_t* len) { struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; - int count; *len = 0; - const char* line = el_gets(elbe->elstate, &count); + if (CLIDebuggerEditLinePoll(be, -1) != 1) { + return NULL; + } + MutexLock(&elbe->promptMutex); + int count = elbe->count; + const char* line = elbe->prompt; + elbe->prompt = NULL; + MutexUnlock(&elbe->promptMutex); if (line) { if (count > 1) { // Crop off newline @@ -126,12 +199,13 @@ const char* _CLIDebuggerEditLineReadLine(struct CLIDebuggerBackend* be, size_t* } return line; } -void _CLIDebuggerEditLineLineAppend(struct CLIDebuggerBackend* be, const char* line) { + +static void CLIDebuggerEditLineLineAppend(struct CLIDebuggerBackend* be, const char* line) { struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; el_insertstr(elbe->elstate, line); } -const char* _CLIDebuggerEditLineHistoryLast(struct CLIDebuggerBackend* be, size_t* len) { +static const char* CLIDebuggerEditLineHistoryLast(struct CLIDebuggerBackend* be, size_t* len) { struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; HistEvent ev; if (history(elbe->histate, &ev, H_FIRST) < 0) { @@ -148,7 +222,7 @@ const char* _CLIDebuggerEditLineHistoryLast(struct CLIDebuggerBackend* be, size_ return ev.str; } -void _CLIDebuggerEditLineHistoryAppend(struct CLIDebuggerBackend* be, const char* line) { +static void CLIDebuggerEditLineHistoryAppend(struct CLIDebuggerBackend* be, const char* line) { struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; HistEvent ev; history(elbe->histate, &ev, H_ENTER, line); @@ -156,13 +230,14 @@ void _CLIDebuggerEditLineHistoryAppend(struct CLIDebuggerBackend* be, const char struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void) { struct CLIDebuggerEditLineBackend* elbe = calloc(1, sizeof(*elbe)); - elbe->d.printf = _CLIDebuggerEditLinePrintf; - elbe->d.init = _CLIDebuggerEditLineInit; - elbe->d.deinit = _CLIDebuggerEditLineDeinit; - elbe->d.readline = _CLIDebuggerEditLineReadLine; - elbe->d.lineAppend = _CLIDebuggerEditLineLineAppend; - elbe->d.historyLast = _CLIDebuggerEditLineHistoryLast; - elbe->d.historyAppend = _CLIDebuggerEditLineHistoryAppend; + elbe->d.printf = CLIDebuggerEditLinePrintf; + elbe->d.init = CLIDebuggerEditLineInit; + elbe->d.deinit = CLIDebuggerEditLineDeinit; + elbe->d.poll = CLIDebuggerEditLinePoll; + elbe->d.readline = CLIDebuggerEditLineReadLine; + elbe->d.lineAppend = CLIDebuggerEditLineLineAppend; + elbe->d.historyLast = CLIDebuggerEditLineHistoryLast; + elbe->d.historyAppend = CLIDebuggerEditLineHistoryAppend; elbe->d.interrupt = NULL; return &elbe->d; } diff --git a/src/feature/editline/cli-el-backend.h b/src/feature/editline/cli-el-backend.h index 89ceec641..312798a38 100644 --- a/src/feature/editline/cli-el-backend.h +++ b/src/feature/editline/cli-el-backend.h @@ -14,13 +14,6 @@ CXX_GUARD_START #include -struct CLIDebuggerEditLineBackend { - struct CLIDebuggerBackend d; - - EditLine* elstate; - History* histate; -}; - struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void); CXX_GUARD_END diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index a0bfe1999..55d44a0ae 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -22,6 +22,7 @@ DebuggerConsoleController::DebuggerConsoleController(QObject* parent) m_backend.printf = printf; m_backend.init = init; m_backend.deinit = deinit; + m_backend.poll = poll; m_backend.readline = readLine; m_backend.lineAppend = lineAppend; m_backend.historyLast = historyLast; @@ -88,6 +89,14 @@ void DebuggerConsoleController::deinit(struct CLIDebuggerBackend* be) { } } +int DebuggerConsoleController::poll(struct CLIDebuggerBackend* be, int32_t timeoutMs) { + Backend* consoleBe = reinterpret_cast(be); + DebuggerConsoleController* self = consoleBe->self; + QMutexLocker lock(&self->m_mutex); + self->m_cond.wait(&self->m_mutex, timeoutMs < 0 ? ULONG_MAX : static_cast(timeoutMs)); + return !self->m_lines.isEmpty(); +} + const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, size_t* len) { Backend* consoleBe = reinterpret_cast(be); DebuggerConsoleController* self = consoleBe->self; @@ -137,10 +146,6 @@ void DebuggerConsoleController::interrupt(struct CLIDebuggerBackend* be) { DebuggerConsoleController* self = consoleBe->self; QMutexLocker lock(&self->m_mutex); self->m_cond.wakeOne(); - if (!self->m_lines.isEmpty()) { - return; - } - self->m_lines.append("\033"); } void DebuggerConsoleController::historyLoad() { diff --git a/src/platform/qt/DebuggerConsoleController.h b/src/platform/qt/DebuggerConsoleController.h index c3d98d35c..b4530185b 100644 --- a/src/platform/qt/DebuggerConsoleController.h +++ b/src/platform/qt/DebuggerConsoleController.h @@ -42,6 +42,7 @@ private: static void printf(struct CLIDebuggerBackend* be, const char* fmt, ...); static void init(struct CLIDebuggerBackend* be); static void deinit(struct CLIDebuggerBackend* be); + static int poll(struct CLIDebuggerBackend* be, int32_t timeoutMs); static const char* readLine(struct CLIDebuggerBackend* be, size_t* len); static void lineAppend(struct CLIDebuggerBackend* be, const char* line); static const char* historyLast(struct CLIDebuggerBackend* be, size_t* len);