diff --git a/gdbstub/gdbstub b/gdbstub/gdbstub index b00d0f81..8ce6abf8 160000 --- a/gdbstub/gdbstub +++ b/gdbstub/gdbstub @@ -1 +1 @@ -Subproject commit b00d0f815b3996a13ce87a4d9d6d7038353e77e6 +Subproject commit 8ce6abf8d32291ad66a3724123a9a583b9a3bb4b diff --git a/gdbstub/gdbstub_sys.c b/gdbstub/gdbstub_sys.c index bfba9bf7..7773de67 100644 --- a/gdbstub/gdbstub_sys.c +++ b/gdbstub/gdbstub_sys.c @@ -3,8 +3,24 @@ #include "libpcsxcore/socket.h" #include "libpcsxcore/r3000a.h" #include +#include +#include + +#ifdef _POSIX_VERSION +#include +#include +#include +#include +#include + +static pthread_t thread; +static mqd_t in_queue, out_queue; +#endif static int server_socket, client_socket; +static enum { + PAUSED, +} state; static void update_regs(struct dbg_state *const dbg_state) { @@ -56,13 +72,6 @@ static void update_regs(struct dbg_state *const dbg_state) dbg_state->registers[DBG_CPU_MIPS_I_REG_PC] = psxRegs.pc; } -void dbg_sys_process(void) -{ - static struct dbg_state dbg_state; - update_regs(&dbg_state); - dbg_main(&dbg_state); -} - int dbg_sys_getc(void) { while (1) { @@ -74,15 +83,22 @@ int dbg_sys_getc(void) case READ_SOCKET_OK: return packet; - case READ_SOCKET_SHUTDOWN: + case READ_SOCKET_SHUTDOWN: { + struct msg msg; + + printf("gdb shutdown\n"); client_socket = 0; + msg.type = MSG_TYPE_SHUTDOWN; + + if (mq_send(out_queue, (const char *)&msg, sizeof msg, 0)) + perror("dbg_sys_getc() mq_send()"); return EOF; + } case READ_SOCKET_ERR_INVALID_ARG: /* Fall through. */ case READ_SOCKET_ERR_RECV: /* Fall through. */ - default: break; } @@ -106,27 +122,247 @@ int dbg_sys_mem_writeb(address addr, char val) return 0; } +#ifdef _POSIX_VERSION +static int wait_hit_or_break(struct msg *msg) +{ + do { + int ret = mq_receive(in_queue, (char *)msg, sizeof *msg, 0); + + if (ret < 0 && errno == EAGAIN) { + /* Breakpoint has not been hit yet, look for incoming messages from gdb. */ + char packet; + size_t len = sizeof packet; + const enum read_socket_err err = ReadSocket(client_socket, &packet, &len); + + switch (err) { + case READ_SOCKET_OK: + if (len && packet == 0x03) + return 0; + + break; + + case READ_SOCKET_SHUTDOWN: + printf("gdb shutdown\n"); + client_socket = 0; + msg->type = MSG_TYPE_SHUTDOWN; + + if (mq_send(out_queue, (const char *)msg, sizeof *msg, 0)) + perror("wait_hit_or_break() mq_send()"); + + return EOF; + + case READ_SOCKET_ERR_INVALID_ARG: + /* Fall through. */ + case READ_SOCKET_ERR_RECV: + /* Fall through. */ + default: + break; + } + } + else if (msg->type != MSG_TYPE_HIT) { + fprintf(stderr, "unexpected msg.type %d\n", msg->type); + return 1; + } + else + return 0; + } while (1); + + return 1; +} +#endif + +#ifdef _POSIX_VERSION int dbg_sys_continue(void) { - return 0; -} + struct msg msg; + msg.type = MSG_TYPE_CONTINUE; + + if (mq_send(out_queue, (const char *)&msg, sizeof msg, 0)) { + perror("dbg_sys_continue(): mq_send()"); + return 1; + } + + return wait_hit_or_break(&msg); +} +#endif + +#ifdef _POSIX_VERSION int dbg_sys_step(void) { + struct msg msg; + + msg.type = MSG_TYPE_STEP; + + if (mq_send(out_queue, (const char *)&msg, sizeof msg, 0)) { + perror("dbg_sys_step(): mq_send()"); + } + + return wait_hit_or_break(&msg); +} +#endif + +#ifdef _POSIX_VERSION +static int wait_ack(struct msg *msg) +{ + int ret; + + do { + ret = mq_receive(in_queue, (char *)msg, sizeof *msg, 0); + } while (ret < 0 && errno == EAGAIN); + + if (msg->type != MSG_TYPE_ACK) { + fprintf(stderr, "unexpected msg.type %d\n", msg->type); + return 1; + } + return 0; } +#endif + +#ifdef _POSIX_VERSION +int dbg_sys_breakpoint(address addr) +{ + struct msg msg; + + msg.type = MSG_TYPE_BREAKPOINT; + msg.data.breakpoint.addr = addr; + + if (mq_send(out_queue, (const char *)&msg, sizeof msg, 0)) { + perror("dbg_sys_breakpoint(): mq_send()"); + } + + return wait_ack(&msg); +} +#endif + +#ifdef _POSIX_VERSION +int dbg_sys_del_breakpoint(address addr) +{ + struct msg msg; + + msg.type = MSG_TYPE_REMOVE_BREAKPOINT; + + if (mq_send(out_queue, (const char *)&msg, sizeof msg, 0)) { + perror("dbg_sys_breakpoint(): mq_send()"); + } + + return wait_ack(&msg); +} +#endif + +#ifdef _POSIX_VERSION +static int queue_create(void) +{ + struct mq_attr attr; + + attr.mq_msgsize = sizeof (struct msg); + attr.mq_flags = 0; + attr.mq_maxmsg = 4; + + mq_unlink("/pcsxrin"); + in_queue = mq_open("/pcsxrin", O_CREAT | O_RDWR | O_EXCL | O_NONBLOCK, 0600, &attr); + + mq_unlink("/pcsxrout"); + out_queue = mq_open("/pcsxrout", O_CREAT | O_RDWR | O_EXCL, 0600, &attr); + + if ((out_queue < 0) || (in_queue < 0)) { + perror("mq_open()"); + return 1; + } + + return 0; +} +#endif + +static int exit_loop; + +static void *loop(void *const args) +{ + struct dbg_state dbg_state = {0}; + + SetsBlock(server_socket); + + if ((client_socket = GetClient(server_socket, 1)) < 0) { + fprintf(stderr, "GetClient() failed\n"); + return NULL; + } + + SetsNonblock(client_socket); + + printf("Accepted gdb connection\n"); + + while (!exit_loop) { + update_regs(&dbg_state); + dbg_main(&dbg_state); + } + + return NULL; +} + +#ifdef _POSIX_VERSION +static void start_thread(void) +{ + if (pthread_create(&thread, NULL, loop, NULL)) + perror("could not start gdb server thread"); +} +#endif + +#ifdef _POSIX_VERSION +static void stop_thread(void) +{ + if (pthread_join(thread, NULL)) + perror("pthread_join()"); + + mq_unlink("/pcsxrin"); + mq_unlink("/pcsxrout"); +} +#endif + +#ifdef _POSIX_VERSION +void gdbstub_sys_recv(struct msg *msg) +{ + const ssize_t sz = mq_receive(out_queue, (char *)msg, sizeof *msg, 0); + + if (sz < 0) + perror("mq_receive"); +} +#endif + +#ifdef _POSIX_VERSION +void gdbstub_sys_send(const struct msg *msg) +{ + if (mq_send(in_queue, (const char *)msg, sizeof *msg, 0)) { + perror("dbg_sys_send(): mq_send()"); + } +} +#endif + +void dbg_stop(void) +{ + exit_loop = 1; + stop_thread(); +} void dbg_start(void) { - const unsigned short port = 3333; + if (server_socket > 0) { + fprintf(stderr, "gdb server already started\n"); + return; + } + else { + const unsigned short port = 3333; - if (server_socket > 0) - StopServer(server_socket); + server_socket = StartServer(port); - server_socket = StartServer(port); - - if (server_socket > 0) - printf("GDB server started on port %hu.\n", port); - else - fprintf(stderr, "Could not start GDB server\n"); + if (server_socket > 0) { + printf("GDB server started on port %hu.\n", port); + if (queue_create()) + fprintf(stderr, "could not create gdb stub internal queues\n"); + else + start_thread(); + } + else + fprintf(stderr, "could not start GDB server\n"); + } } diff --git a/gdbstub/gdbstub_sys.h b/gdbstub/gdbstub_sys.h index 84bae1fa..f44dfcc9 100644 --- a/gdbstub/gdbstub_sys.h +++ b/gdbstub/gdbstub_sys.h @@ -42,7 +42,11 @@ enum DBG_REGISTER { DBG_CPU_MIPS_I_REG_BAD, DBG_CPU_MIPS_I_REG_CAUSE, DBG_CPU_MIPS_I_REG_PC, - DBG_CPU_NUM_REGISTERS + + /* GDB requests 73, where 38 are the ones above and the rest + * are the floating-point registers. This way, unused registers + * are left to zero. */ + DBG_CPU_NUM_REGISTERS = 73 }; typedef unsigned int reg; @@ -52,7 +56,29 @@ struct dbg_state { reg registers[DBG_CPU_NUM_REGISTERS]; }; +struct msg { + enum { + MSG_TYPE_CONTINUE, + MSG_TYPE_BREAKPOINT, + MSG_TYPE_STEP, + MSG_TYPE_ACK, + MSG_TYPE_REMOVE_BREAKPOINT, + MSG_TYPE_SHUTDOWN, + + /* Response frames. */ + MSG_TYPE_HIT + } type; + + union { + struct { + address addr; + } breakpoint; + } data; +}; + void dbg_start(void); -void dbg_sys_process(void); +void dbg_stop(void); +void gdbstub_sys_send(const struct msg *msg); +void gdbstub_sys_recv(struct msg *msg); #endif /* GDBSTUB_SYS_H */ diff --git a/gui/LnxMain.c b/gui/LnxMain.c index 12aeb5fc..2e087ee3 100644 --- a/gui/LnxMain.c +++ b/gui/LnxMain.c @@ -321,6 +321,11 @@ int main(int argc, char *argv[]) { SetIsoFile(isofilename); runcd = RUN_CD; } + else if (!strcmp(argv[i], "-gdb")) { + /* Force configuration. */ + Config.Cpu = CPU_INTERPRETER; + Config.GdbServer = 1; + } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) { @@ -461,6 +466,8 @@ int main(int argc, char *argv[]) { psxCpu->Execute(); } + if (Config.GdbServer) dbg_stop(); + return 0; } diff --git a/libpcsxcore/debug.c b/libpcsxcore/debug.c index 463df73b..ecc9edc7 100644 --- a/libpcsxcore/debug.c +++ b/libpcsxcore/debug.c @@ -397,7 +397,7 @@ void DebugVSync() { if (client_socket < 1) { - client_socket = GetClient(server_socket); + client_socket = GetClient(server_socket, 0); if (client_socket > 0) { @@ -478,7 +478,7 @@ void ProcessDebug() { if (client_socket < 1) { - client_socket = GetClient(server_socket); + client_socket = GetClient(server_socket, 0); if (client_socket > 0) { diff --git a/libpcsxcore/psxinterpreter.c b/libpcsxcore/psxinterpreter.c index a2bc0e1d..941fda4b 100644 --- a/libpcsxcore/psxinterpreter.c +++ b/libpcsxcore/psxinterpreter.c @@ -1198,6 +1198,68 @@ static void intClear(u32 Addr, u32 Size) { static void intShutdown() { } +static void process_gdb(void) { + static int shutdown; + static u32 tgt_addr; + static int step, must_continue; + struct msg msg; + + if (shutdown) + return; + + if (step || (must_continue && tgt_addr && tgt_addr == psxRegs.pc)) { + msg.type = MSG_TYPE_HIT; +#if DEBUG == 1 + printf("hit address 0x%08X\n", psxRegs.pc); +#endif + gdbstub_sys_send(&msg); + must_continue = 0; + step = 0; + } + + if (!must_continue) { + gdbstub_sys_recv(&msg); + + switch (msg.type) { + case MSG_TYPE_CONTINUE: + must_continue = 1; + break; + + case MSG_TYPE_STEP: + step = 1; + break; + + case MSG_TYPE_REMOVE_BREAKPOINT: { + struct msg out; + + tgt_addr = 0; + out.type = MSG_TYPE_ACK; + + gdbstub_sys_send(&out); + } + break; + + case MSG_TYPE_BREAKPOINT: { + struct msg out; + + tgt_addr = msg.data.breakpoint.addr; + out.type = MSG_TYPE_ACK; + + gdbstub_sys_send(&out); + } + break; + + case MSG_TYPE_SHUTDOWN: + shutdown = 1; + break; + + default: + fprintf(stderr, "unknown msg.type %d\n", msg.type); + break; + } + } +} + // interpreter execution static inline void execI() { u32 *code = Read_ICache(psxRegs.pc, FALSE); @@ -1205,7 +1267,7 @@ static inline void execI() { debugI(); - if (Config.GdbServer) dbg_sys_process(); + if (Config.GdbServer) process_gdb(); else if (Config.Debug) ProcessDebug(); psxRegs.pc += 4; diff --git a/libpcsxcore/socket.c b/libpcsxcore/socket.c index 51aefdf7..eec4c96a 100644 --- a/libpcsxcore/socket.c +++ b/libpcsxcore/socket.c @@ -88,7 +88,7 @@ void StopServer(int s_socket) { #endif } -int GetClient(int s_socket) { +int GetClient(int s_socket, int blocking) { int new_socket = accept(s_socket, NULL, NULL); #ifdef _WIN32 @@ -100,6 +100,7 @@ int GetClient(int s_socket) { #endif #ifndef _WIN32 + if (!blocking) { int flags; flags = fcntl(new_socket, F_GETFL, 0); diff --git a/libpcsxcore/socket.h b/libpcsxcore/socket.h index 531a6485..852c76a4 100644 --- a/libpcsxcore/socket.h +++ b/libpcsxcore/socket.h @@ -35,7 +35,7 @@ enum read_socket_err int StartServer(unsigned short port); void StopServer(int s_socket); -int GetClient(int s_socket); +int GetClient(int s_socket, int blocking); void CloseClient(int client_socket); int HasClient(int client_socket); diff --git a/win32/gui/WndMain.c b/win32/gui/WndMain.c index 374b1877..4c01b200 100644 --- a/win32/gui/WndMain.c +++ b/win32/gui/WndMain.c @@ -238,6 +238,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine RunGui(); + if (Config.GdbServer) dbg_stop(); + return 0; } @@ -1570,7 +1572,7 @@ BOOL CALLBACK ConfigureCpuDlgProc(HWND hW, UINT uMsg, WPARAM wParam, LPARAM lPar } if (Config.GdbServer) { - GdbStartServer(); + dbg_start(); } SaveConfig(); @@ -1996,8 +1998,7 @@ int SysInit() { LoadMcds(Config.Mcd1, Config.Mcd2); if (Config.Debug) StartDebugger(); - - if (Config.GdbServer) GdbStartServer(); + else if (Config.GdbServer) dbg_start(); return 0; }