mirror of https://github.com/mgba-emu/mgba.git
Debugger: Add debugger polling to avoid blocking
This commit is contained in:
parent
a00f2939ad
commit
257122796c
|
@ -152,7 +152,7 @@ struct mDebuggerModule {
|
||||||
void (*init)(struct mDebuggerModule*);
|
void (*init)(struct mDebuggerModule*);
|
||||||
void (*deinit)(struct mDebuggerModule*);
|
void (*deinit)(struct mDebuggerModule*);
|
||||||
|
|
||||||
void (*paused)(struct mDebuggerModule*);
|
void (*paused)(struct mDebuggerModule*, int32_t timeoutMs);
|
||||||
void (*update)(struct mDebuggerModule*);
|
void (*update)(struct mDebuggerModule*);
|
||||||
void (*entered)(struct mDebuggerModule*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
void (*entered)(struct mDebuggerModule*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||||
void (*custom)(struct mDebuggerModule*);
|
void (*custom)(struct mDebuggerModule*);
|
||||||
|
@ -166,6 +166,7 @@ void mDebuggerDeinit(struct mDebugger*);
|
||||||
void mDebuggerAttach(struct mDebugger*, struct mCore*);
|
void mDebuggerAttach(struct mDebugger*, struct mCore*);
|
||||||
void mDebuggerAttachModule(struct mDebugger*, struct mDebuggerModule*);
|
void mDebuggerAttachModule(struct mDebugger*, struct mDebuggerModule*);
|
||||||
void mDebuggerDetachModule(struct mDebugger*, struct mDebuggerModule*);
|
void mDebuggerDetachModule(struct mDebugger*, struct mDebuggerModule*);
|
||||||
|
void mDebuggerRunTimeout(struct mDebugger* debugger, int32_t timeoutMs);
|
||||||
void mDebuggerRun(struct mDebugger*);
|
void mDebuggerRun(struct mDebugger*);
|
||||||
void mDebuggerRunFrame(struct mDebugger*);
|
void mDebuggerRunFrame(struct mDebugger*);
|
||||||
void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||||
|
|
|
@ -73,6 +73,7 @@ struct CLIDebuggerBackend {
|
||||||
|
|
||||||
ATTRIBUTE_FORMAT(printf, 2, 3)
|
ATTRIBUTE_FORMAT(printf, 2, 3)
|
||||||
void (*printf)(struct CLIDebuggerBackend*, const char* fmt, ...);
|
void (*printf)(struct CLIDebuggerBackend*, const char* fmt, ...);
|
||||||
|
int (*poll)(struct CLIDebuggerBackend*, int32_t timeoutMs);
|
||||||
const char* (*readline)(struct CLIDebuggerBackend*, size_t* len);
|
const char* (*readline)(struct CLIDebuggerBackend*, size_t* len);
|
||||||
void (*lineAppend)(struct CLIDebuggerBackend*, const char* line);
|
void (*lineAppend)(struct CLIDebuggerBackend*, const char* line);
|
||||||
const char* (*historyLast)(struct CLIDebuggerBackend*, size_t* len);
|
const char* (*historyLast)(struct CLIDebuggerBackend*, size_t* len);
|
||||||
|
|
|
@ -41,7 +41,6 @@ struct GDBStub {
|
||||||
Socket socket;
|
Socket socket;
|
||||||
Socket connection;
|
Socket connection;
|
||||||
|
|
||||||
bool shouldBlock;
|
|
||||||
int untilPoll;
|
int untilPoll;
|
||||||
|
|
||||||
bool supportsSwbreak;
|
bool supportsSwbreak;
|
||||||
|
@ -56,7 +55,7 @@ bool GDBStubListen(struct GDBStub*, int port, const struct Address* bindAddress,
|
||||||
void GDBStubHangup(struct GDBStub*);
|
void GDBStubHangup(struct GDBStub*);
|
||||||
void GDBStubShutdown(struct GDBStub*);
|
void GDBStubShutdown(struct GDBStub*);
|
||||||
|
|
||||||
void GDBStubUpdate(struct GDBStub*);
|
bool GDBStubUpdate(struct GDBStub*, int timeoutMs);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
|
|
@ -1092,7 +1092,7 @@ bool CLIDebuggerRunCommand(struct CLIDebugger* debugger, const char* line, size_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _commandLine(struct mDebuggerModule* debugger) {
|
static void _commandLine(struct mDebuggerModule* debugger, int32_t timeoutMs) {
|
||||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||||
const char* line;
|
const char* line;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -1102,15 +1102,20 @@ static void _commandLine(struct mDebuggerModule* debugger) {
|
||||||
_printStatus(cliDebugger, 0);
|
_printStatus(cliDebugger, 0);
|
||||||
}
|
}
|
||||||
while (debugger->isPaused && !mDebuggerIsShutdown(debugger->p)) {
|
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);
|
line = cliDebugger->backend->readline(cliDebugger->backend, &len);
|
||||||
if (!line || len == 0) {
|
if (!line || len == 0) {
|
||||||
mDebuggerShutdown(debugger->p);
|
mDebuggerShutdown(debugger->p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (line[0] == '\033') {
|
|
||||||
cliDebugger->skipStatus = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (line[0] == '\n') {
|
if (line[0] == '\n') {
|
||||||
line = cliDebugger->backend->historyLast(cliDebugger->backend, &len);
|
line = cliDebugger->backend->historyLast(cliDebugger->backend, &len);
|
||||||
if (line && len) {
|
if (line && len) {
|
||||||
|
|
|
@ -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 i;
|
||||||
size_t anyPaused = 0;
|
size_t anyPaused = 0;
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ void mDebuggerRun(struct mDebugger* debugger) {
|
||||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||||
if (module->isPaused) {
|
if (module->isPaused) {
|
||||||
if (module->paused) {
|
if (module->paused) {
|
||||||
module->paused(module);
|
module->paused(module, timeoutMs);
|
||||||
}
|
}
|
||||||
if (module->isPaused) {
|
if (module->isPaused) {
|
||||||
++anyPaused;
|
++anyPaused;
|
||||||
|
@ -167,6 +167,10 @@ void mDebuggerRun(struct mDebugger* debugger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mDebuggerRun(struct mDebugger* debugger) {
|
||||||
|
mDebuggerRunTimeout(debugger, 50);
|
||||||
|
}
|
||||||
|
|
||||||
void mDebuggerRunFrame(struct mDebugger* debugger) {
|
void mDebuggerRunFrame(struct mDebugger* debugger) {
|
||||||
uint32_t frame = debugger->core->frameCounter(debugger->core);
|
uint32_t frame = debugger->core->frameCounter(debugger->core);
|
||||||
do {
|
do {
|
||||||
|
@ -309,7 +313,5 @@ bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int
|
||||||
|
|
||||||
void mDebuggerModuleSetNeedsCallback(struct mDebuggerModule* debugger) {
|
void mDebuggerModuleSetNeedsCallback(struct mDebuggerModule* debugger) {
|
||||||
debugger->needsCallback = true;
|
debugger->needsCallback = true;
|
||||||
if (debugger->p->state == DEBUGGER_RUNNING) {
|
mDebuggerUpdatePaused(debugger->p);
|
||||||
debugger->p->state = DEBUGGER_CALLBACK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
#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,
|
||||||
|
@ -135,20 +133,17 @@ static void _gdbStubPoll(struct mDebuggerModule* debugger) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||||
stub->shouldBlock = false;
|
GDBStubUpdate(stub, 0);
|
||||||
GDBStubUpdate(stub);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gdbStubWait(struct mDebuggerModule* debugger) {
|
static void _gdbStubWait(struct mDebuggerModule* debugger, int32_t timeoutMs) {
|
||||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||||
stub->shouldBlock = true;
|
GDBStubUpdate(stub, timeoutMs);
|
||||||
GDBStubUpdate(stub);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gdbStubUpdate(struct mDebuggerModule* debugger) {
|
static void _gdbStubUpdate(struct mDebuggerModule* debugger) {
|
||||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||||
stub->shouldBlock = false;
|
GDBStubUpdate(stub, 0);
|
||||||
GDBStubUpdate(stub);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _ack(struct GDBStub* stub) {
|
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) {
|
static void _continue(struct GDBStub* stub, const char* message) {
|
||||||
mDebuggerModuleSetNeedsCallback(&stub->d);
|
mDebuggerModuleSetNeedsCallback(&stub->d);
|
||||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||||
|
stub->d.isPaused = false;
|
||||||
// TODO: parse message
|
// TODO: parse message
|
||||||
UNUSED(message);
|
UNUSED(message);
|
||||||
}
|
}
|
||||||
|
@ -791,7 +787,6 @@ void GDBStubCreate(struct GDBStub* stub) {
|
||||||
stub->d.type = DEBUGGER_GDB;
|
stub->d.type = DEBUGGER_GDB;
|
||||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||||
stub->lineAck = GDB_ACK_PENDING;
|
stub->lineAck = GDB_ACK_PENDING;
|
||||||
stub->shouldBlock = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress, enum GDBWatchpointsBehvaior watchpointsBehavior) {
|
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) {
|
if (stub->socket == INVALID_SOCKET) {
|
||||||
stub->d.needsCallback = false;
|
stub->d.needsCallback = false;
|
||||||
stub->d.isPaused = false;
|
stub->d.isPaused = false;
|
||||||
mDebuggerUpdatePaused(stub->d.p);
|
mDebuggerUpdatePaused(stub->d.p);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (stub->connection == INVALID_SOCKET) {
|
if (stub->connection == INVALID_SOCKET) {
|
||||||
if (stub->shouldBlock) {
|
if (timeoutMs) {
|
||||||
Socket reads = stub->socket;
|
Socket reads = stub->socket;
|
||||||
SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
|
SocketPoll(1, &reads, 0, 0, timeoutMs);
|
||||||
}
|
}
|
||||||
stub->connection = SocketAccept(stub->socket, 0);
|
stub->connection = SocketAccept(stub->socket, 0);
|
||||||
if (!SOCKET_FAILED(stub->connection)) {
|
if (!SOCKET_FAILED(stub->connection)) {
|
||||||
|
@ -860,36 +855,38 @@ void GDBStubUpdate(struct GDBStub* stub) {
|
||||||
}
|
}
|
||||||
mDebuggerEnter(stub->d.p, DEBUGGER_ENTER_ATTACHED, 0);
|
mDebuggerEnter(stub->d.p, DEBUGGER_ENTER_ATTACHED, 0);
|
||||||
} else if (SocketWouldBlock()) {
|
} else if (SocketWouldBlock()) {
|
||||||
return;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
goto connectionLost;
|
goto connectionLost;
|
||||||
}
|
}
|
||||||
SocketSetTCPPush(stub->connection, 1);
|
SocketSetTCPPush(stub->connection, 1);
|
||||||
}
|
}
|
||||||
while (true) {
|
|
||||||
if (stub->shouldBlock) {
|
if (timeoutMs) {
|
||||||
Socket reads = stub->connection;
|
Socket reads = stub->connection;
|
||||||
SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
|
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;
|
|
||||||
}
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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:
|
connectionLost:
|
||||||
mLOG(DEBUGGER, WARN, "Connection lost");
|
mLOG(DEBUGGER, WARN, "Connection lost");
|
||||||
GDBStubHangup(stub);
|
GDBStubHangup(stub);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,31 @@
|
||||||
|
|
||||||
#include <mgba/core/config.h>
|
#include <mgba/core/config.h>
|
||||||
#include <mgba/core/version.h>
|
#include <mgba/core/version.h>
|
||||||
|
#include <mgba-util/threading.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
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 struct CLIDebugger* _activeDebugger;
|
||||||
|
|
||||||
|
static THREAD_ENTRY _promptThread(void*);
|
||||||
|
|
||||||
static char* _prompt(EditLine* el) {
|
static char* _prompt(EditLine* el) {
|
||||||
UNUSED(el);
|
UNUSED(el);
|
||||||
return "> ";
|
return "> ";
|
||||||
|
@ -43,7 +62,7 @@ static unsigned char _tabComplete(EditLine* elstate, int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ATTRIBUTE_FORMAT(printf, 2, 3)
|
ATTRIBUTE_FORMAT(printf, 2, 3)
|
||||||
void _CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt, ...) {
|
static void CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt, ...) {
|
||||||
UNUSED(be);
|
UNUSED(be);
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
|
@ -51,7 +70,7 @@ void _CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt,
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) {
|
static void CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) {
|
||||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||||
// TODO: get argv[0]
|
// TODO: get argv[0]
|
||||||
elbe->elstate = el_init(binaryName, stdin, stdout, stderr);
|
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;
|
_activeDebugger = be->p;
|
||||||
signal(SIGINT, _breakIntoDefault);
|
signal(SIGINT, _breakIntoDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) {
|
static void CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) {
|
||||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) 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];
|
char path[PATH_MAX + 1];
|
||||||
mCoreConfigDirectory(path, PATH_MAX);
|
mCoreConfigDirectory(path, PATH_MAX);
|
||||||
if (path[0]) {
|
if (path[0]) {
|
||||||
|
@ -111,11 +144,51 @@ void _CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) {
|
||||||
free(elbe);
|
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;
|
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||||
int count;
|
|
||||||
*len = 0;
|
*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 (line) {
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
// Crop off newline
|
// Crop off newline
|
||||||
|
@ -126,12 +199,13 @@ const char* _CLIDebuggerEditLineReadLine(struct CLIDebuggerBackend* be, size_t*
|
||||||
}
|
}
|
||||||
return line;
|
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;
|
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||||
el_insertstr(elbe->elstate, line);
|
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;
|
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||||
HistEvent ev;
|
HistEvent ev;
|
||||||
if (history(elbe->histate, &ev, H_FIRST) < 0) {
|
if (history(elbe->histate, &ev, H_FIRST) < 0) {
|
||||||
|
@ -148,7 +222,7 @@ const char* _CLIDebuggerEditLineHistoryLast(struct CLIDebuggerBackend* be, size_
|
||||||
return ev.str;
|
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;
|
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||||
HistEvent ev;
|
HistEvent ev;
|
||||||
history(elbe->histate, &ev, H_ENTER, line);
|
history(elbe->histate, &ev, H_ENTER, line);
|
||||||
|
@ -156,13 +230,14 @@ void _CLIDebuggerEditLineHistoryAppend(struct CLIDebuggerBackend* be, const char
|
||||||
|
|
||||||
struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void) {
|
struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void) {
|
||||||
struct CLIDebuggerEditLineBackend* elbe = calloc(1, sizeof(*elbe));
|
struct CLIDebuggerEditLineBackend* elbe = calloc(1, sizeof(*elbe));
|
||||||
elbe->d.printf = _CLIDebuggerEditLinePrintf;
|
elbe->d.printf = CLIDebuggerEditLinePrintf;
|
||||||
elbe->d.init = _CLIDebuggerEditLineInit;
|
elbe->d.init = CLIDebuggerEditLineInit;
|
||||||
elbe->d.deinit = _CLIDebuggerEditLineDeinit;
|
elbe->d.deinit = CLIDebuggerEditLineDeinit;
|
||||||
elbe->d.readline = _CLIDebuggerEditLineReadLine;
|
elbe->d.poll = CLIDebuggerEditLinePoll;
|
||||||
elbe->d.lineAppend = _CLIDebuggerEditLineLineAppend;
|
elbe->d.readline = CLIDebuggerEditLineReadLine;
|
||||||
elbe->d.historyLast = _CLIDebuggerEditLineHistoryLast;
|
elbe->d.lineAppend = CLIDebuggerEditLineLineAppend;
|
||||||
elbe->d.historyAppend = _CLIDebuggerEditLineHistoryAppend;
|
elbe->d.historyLast = CLIDebuggerEditLineHistoryLast;
|
||||||
|
elbe->d.historyAppend = CLIDebuggerEditLineHistoryAppend;
|
||||||
elbe->d.interrupt = NULL;
|
elbe->d.interrupt = NULL;
|
||||||
return &elbe->d;
|
return &elbe->d;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,6 @@ CXX_GUARD_START
|
||||||
|
|
||||||
#include <histedit.h>
|
#include <histedit.h>
|
||||||
|
|
||||||
struct CLIDebuggerEditLineBackend {
|
|
||||||
struct CLIDebuggerBackend d;
|
|
||||||
|
|
||||||
EditLine* elstate;
|
|
||||||
History* histate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void);
|
struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
|
@ -22,6 +22,7 @@ DebuggerConsoleController::DebuggerConsoleController(QObject* parent)
|
||||||
m_backend.printf = printf;
|
m_backend.printf = printf;
|
||||||
m_backend.init = init;
|
m_backend.init = init;
|
||||||
m_backend.deinit = deinit;
|
m_backend.deinit = deinit;
|
||||||
|
m_backend.poll = poll;
|
||||||
m_backend.readline = readLine;
|
m_backend.readline = readLine;
|
||||||
m_backend.lineAppend = lineAppend;
|
m_backend.lineAppend = lineAppend;
|
||||||
m_backend.historyLast = historyLast;
|
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<Backend*>(be);
|
||||||
|
DebuggerConsoleController* self = consoleBe->self;
|
||||||
|
QMutexLocker lock(&self->m_mutex);
|
||||||
|
self->m_cond.wait(&self->m_mutex, timeoutMs < 0 ? ULONG_MAX : static_cast<unsigned long>(timeoutMs));
|
||||||
|
return !self->m_lines.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, size_t* len) {
|
const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, size_t* len) {
|
||||||
Backend* consoleBe = reinterpret_cast<Backend*>(be);
|
Backend* consoleBe = reinterpret_cast<Backend*>(be);
|
||||||
DebuggerConsoleController* self = consoleBe->self;
|
DebuggerConsoleController* self = consoleBe->self;
|
||||||
|
@ -137,10 +146,6 @@ void DebuggerConsoleController::interrupt(struct CLIDebuggerBackend* be) {
|
||||||
DebuggerConsoleController* self = consoleBe->self;
|
DebuggerConsoleController* self = consoleBe->self;
|
||||||
QMutexLocker lock(&self->m_mutex);
|
QMutexLocker lock(&self->m_mutex);
|
||||||
self->m_cond.wakeOne();
|
self->m_cond.wakeOne();
|
||||||
if (!self->m_lines.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self->m_lines.append("\033");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerConsoleController::historyLoad() {
|
void DebuggerConsoleController::historyLoad() {
|
||||||
|
|
|
@ -42,6 +42,7 @@ private:
|
||||||
static void printf(struct CLIDebuggerBackend* be, const char* fmt, ...);
|
static void printf(struct CLIDebuggerBackend* be, const char* fmt, ...);
|
||||||
static void init(struct CLIDebuggerBackend* be);
|
static void init(struct CLIDebuggerBackend* be);
|
||||||
static void deinit(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 const char* readLine(struct CLIDebuggerBackend* be, size_t* len);
|
||||||
static void lineAppend(struct CLIDebuggerBackend* be, const char* line);
|
static void lineAppend(struct CLIDebuggerBackend* be, const char* line);
|
||||||
static const char* historyLast(struct CLIDebuggerBackend* be, size_t* len);
|
static const char* historyLast(struct CLIDebuggerBackend* be, size_t* len);
|
||||||
|
|
Loading…
Reference in New Issue