From 70a1727bd57a6aa59455ba80c26984c63070b8aa Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 23 Oct 2021 21:15:30 +0000 Subject: [PATCH 1/6] gdbstub: improve readability by using less ifdef WIN32 --- desmume/src/gdbstub/gdbstub.cpp | 112 +++++++++++--------------------- 1 file changed, 38 insertions(+), 74 deletions(-) diff --git a/desmume/src/gdbstub/gdbstub.cpp b/desmume/src/gdbstub/gdbstub.cpp index 2f7751644..6d14afb7f 100644 --- a/desmume/src/gdbstub/gdbstub.cpp +++ b/desmume/src/gdbstub/gdbstub.cpp @@ -48,6 +48,30 @@ #endif #endif // HOST_WINDOWS +#ifdef WIN32 +#define RECV(A,B,C) recv(A, B, C, 0) +#define SEND(A,B,C) send(A, (char*)B, C, 0) +#define CLOSESOCKET(A) closesocket(A) +#define SOCKLEN_T int +static int set_socket_nodelay(int socket) { + BOOL nodelay_opt = 1; + return setsockopt( socket, IPPROTO_TCP, TCP_NODELAY, + (char*)&nodelay_opt, sizeof( nodelay_opt)); +} +#else +#define INVALID_SOCKET -1 +#define RECV(A,B,C) read(A, (char*)B, C) +#define SEND(A,B,C) write(A, B, C) +#define CLOSESOCKET(A) close(A) +#define SOCKLEN_T socklen_t +static int set_socket_nodelay(int socket) { + int nodelay_opt = 1; + return setsockopt( socket, IPPROTO_TCP, TCP_NODELAY, + &nodelay_opt, sizeof( nodelay_opt)); +} +#endif + + slock *cpu_mutex = NULL; #ifdef __GNUC__ @@ -187,23 +211,13 @@ void gdbstub_mutex_unlock() static void causeQuit_gdb( struct gdb_stub_state *stub) { uint8_t command = QUIT_STUB_MESSAGE; - -#ifdef WIN32 - send( stub->ctl_pipe[1], (char*)&command, 1, 0); -#else - write( stub->ctl_pipe[1], &command, 1); -#endif + SEND( stub->ctl_pipe[1], &command, 1); } static void indicateCPUStop_gdb( struct gdb_stub_state *stub) { uint8_t command = CPU_STOPPED_STUB_MESSAGE; - -#ifdef WIN32 - send( stub->ctl_pipe[1], (char*)&command, 1, 0); -#else - write( stub->ctl_pipe[1], &command, 1); -#endif + SEND( stub->ctl_pipe[1], &command, 1); } @@ -974,29 +988,17 @@ createSocket ( int port) { sock = socket (PF_INET, SOCK_STREAM, 0); -#ifdef WIN32 if ( sock != INVALID_SOCKET) -#else - if (sock != -1) -#endif { if (bind (sock, (struct sockaddr *) &bind_addr, sizeof (bind_addr)) == -1) { LOG_ERROR("Bind failed \"%s\" port %d\n", strerror( errno), port); -#ifdef WIN32 - closesocket( sock); -#else - close (sock); -#endif + CLOSESOCKET( sock); sock = -1; } else if (listen (sock, 5) == -1) { LOG_ERROR("Listen failed \"%s\"\n", strerror( errno)); -#ifdef WIN32 - closesocket( sock); -#else - close (sock); -#endif + CLOSESOCKET( sock); sock = -1; } } @@ -1083,11 +1085,7 @@ WINAPI listenerThread_gdb( void *data) { uint8_t ctl_command; //DEBUG_LOG("Control message\n"); -#ifdef WIN32 - recv( state->ctl_pipe[0], (char*)&ctl_command, 1, 0); -#else - read( state->ctl_pipe[0], (char*)&ctl_command, 1); -#endif + RECV( state->ctl_pipe[0], &ctl_command, 1); switch ( ctl_command) { @@ -1131,11 +1129,7 @@ WINAPI listenerThread_gdb( void *data) { if ( FD_ISSET( arm_listener, &read_sock_set)) { SOCKET_TYPE new_conn; struct sockaddr_in gdb_addr; -#ifdef WIN32 - int addr_size = sizeof( gdb_addr); -#else - socklen_t addr_size = sizeof( gdb_addr); -#endif + SOCKLEN_T addr_size = sizeof( gdb_addr); //DEBUG_LOG("listener\n"); @@ -1148,24 +1142,14 @@ WINAPI listenerThread_gdb( void *data) { //DEBUG_LOG("got new socket\n"); if ( state->sock_fd == -1 && state->active) { -#ifdef WIN32 - BOOL nodelay_opt = 1; -#else - int nodelay_opt = 1; -#endif + int set_res; //DEBUG_LOG("new connection\n"); close_sock = 0; /* make the socket NODELAY */ -#ifdef WIN32 - set_res = setsockopt( new_conn, IPPROTO_TCP, TCP_NODELAY, - (char*)&nodelay_opt, sizeof( nodelay_opt)); -#else - set_res = setsockopt( new_conn, IPPROTO_TCP, TCP_NODELAY, - &nodelay_opt, sizeof( nodelay_opt)); -#endif + set_res = set_socket_nodelay( new_conn); if ( set_res != 0) { LOG_ERROR( "Failed to set NODELAY socket option \"%s\"\n", strerror( errno)); @@ -1177,11 +1161,7 @@ WINAPI listenerThread_gdb( void *data) { if ( close_sock) { //DEBUG_LOG("closing new socket\n"); -#ifdef WIN32 - closesocket( new_conn); -#else - close( new_conn); -#endif + CLOSESOCKET( new_conn); } } } @@ -1201,11 +1181,7 @@ WINAPI listenerThread_gdb( void *data) { case READ_SOCKET_ERROR: /* close the socket */ -#ifdef WIN32 - closesocket( gdb_sock); -#else - close( gdb_sock); -#endif + CLOSESOCKET( gdb_sock); state->sock_fd = -1; FD_CLR( gdb_sock, &main_set); break; @@ -1259,11 +1235,7 @@ WINAPI listenerThread_gdb( void *data) { } if ( close_socket) { -#ifdef WIN32 - closesocket( gdb_sock); -#else - close( gdb_sock); -#endif + CLOSESOCKET( gdb_sock); state->sock_fd = -1; FD_CLR( gdb_sock, &main_set); } @@ -1279,19 +1251,11 @@ WINAPI listenerThread_gdb( void *data) { /* tidy up and leave */ if ( state->sock_fd != -1) { -#ifdef WIN32 - closesocket( state->sock_fd); -#else - close( state->sock_fd); -#endif + CLOSESOCKET( state->sock_fd); } /* close the listenering sockets */ -#ifdef WIN32 - closesocket( state->listen_fd); -#else - close( state->listen_fd); -#endif + CLOSESOCKET( state->listen_fd); return; } @@ -1582,7 +1546,7 @@ createStub_gdb( uint16_t port, if ( res != 0) { LOG_ERROR( "Failed to create control socket\n"); } -#else +#else /* not WIN32 */ /* create the control pipe */ res = pipe( stub->ctl_pipe); From 747f7b114b4441b59fd6be32c92a7e19a0316204 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 23 Oct 2021 21:43:38 +0000 Subject: [PATCH 2/6] NDSSystem: replace GDB_STUB ifdefs with macro --- desmume/src/NDSSystem.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index 115a9aede..fbb244f7e 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -67,6 +67,11 @@ extern HWND DisViewWnd[2]; #ifdef GDB_STUB #include "gdbstub.h" +#define GDBSTUB_MUTEX_LOCK() gdbstub_mutex_lock() +#define GDBSTUB_MUTEX_UNLOCK() gdbstub_mutex_unlock() +#else +#define GDBSTUB_MUTEX_LOCK() do {} while(0) +#define GDBSTUB_MUTEX_UNLOCK() do {} while(0) #endif //int xxctr=0; @@ -2056,9 +2061,7 @@ void NDS_debug_step() template void NDS_exec(s32 nb) { - #ifdef GDB_STUB - gdbstub_mutex_lock(); - #endif + GDBSTUB_MUTEX_LOCK(); LagFrameFlag=1; @@ -2089,19 +2092,15 @@ void NDS_exec(s32 nb) if ((NDS_ARM9.stalled || NDS_ARM7.stalled) && execute) { driver->EMU_DebugIdleEnter(); - + while((NDS_ARM9.stalled || NDS_ARM7.stalled) && execute) { - #ifdef GDB_STUB - gdbstub_mutex_unlock(); - #endif + GDBSTUB_MUTEX_UNLOCK(); driver->EMU_DebugIdleUpdate(); - #ifdef GDB_STUB - gdbstub_mutex_lock(); - #endif + GDBSTUB_MUTEX_LOCK(); nds_debug_continuing[0] = nds_debug_continuing[1] = true; } - + driver->EMU_DebugIdleWakeUp(); } #endif @@ -2199,9 +2198,7 @@ void NDS_exec(s32 nb) DEBUG_Notify.NextFrame(); if(cheats) cheats->process(CHEAT_TYPE_INTERNAL); - #ifdef GDB_STUB - gdbstub_mutex_unlock(); - #endif + GDBSTUB_MUTEX_UNLOCK(); } template static void execHardware_interrupts_core() From dbc1f066629ec3cef5258d28ac3b1755b48429a8 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 23 Oct 2021 23:29:24 +0000 Subject: [PATCH 3/6] gdbstub: remove more WIN32 ifdefs, refactor pipe setup code to be re-usable --- desmume/src/gdbstub/gdbstub.cpp | 235 +++++++++++++++----------------- 1 file changed, 109 insertions(+), 126 deletions(-) diff --git a/desmume/src/gdbstub/gdbstub.cpp b/desmume/src/gdbstub/gdbstub.cpp index 6d14afb7f..c05b1bd07 100644 --- a/desmume/src/gdbstub/gdbstub.cpp +++ b/desmume/src/gdbstub/gdbstub.cpp @@ -48,29 +48,6 @@ #endif #endif // HOST_WINDOWS -#ifdef WIN32 -#define RECV(A,B,C) recv(A, B, C, 0) -#define SEND(A,B,C) send(A, (char*)B, C, 0) -#define CLOSESOCKET(A) closesocket(A) -#define SOCKLEN_T int -static int set_socket_nodelay(int socket) { - BOOL nodelay_opt = 1; - return setsockopt( socket, IPPROTO_TCP, TCP_NODELAY, - (char*)&nodelay_opt, sizeof( nodelay_opt)); -} -#else -#define INVALID_SOCKET -1 -#define RECV(A,B,C) read(A, (char*)B, C) -#define SEND(A,B,C) write(A, B, C) -#define CLOSESOCKET(A) close(A) -#define SOCKLEN_T socklen_t -static int set_socket_nodelay(int socket) { - int nodelay_opt = 1; - return setsockopt( socket, IPPROTO_TCP, TCP_NODELAY, - &nodelay_opt, sizeof( nodelay_opt)); -} -#endif - slock *cpu_mutex = NULL; @@ -92,6 +69,113 @@ slock *cpu_mutex = NULL; #define LOG_ERROR( fmt, ...) #endif +#ifdef WIN32 +#define RECV(A,B,C) recv(A, B, C, 0) +#define SEND(A,B,C) send(A, (char*)B, C, 0) +#define CLOSESOCKET(A) closesocket(A) +#define SOCKLEN_T int +static int set_socket_nodelay(int socket) { + BOOL nodelay_opt = 1; + return setsockopt( socket, IPPROTO_TCP, TCP_NODELAY, + (char*)&nodelay_opt, sizeof( nodelay_opt)); +} + +static int INIT_SOCKETS(void) { + /* initialise the winsock library */ + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD( 2, 2 ); + return WSAStartup( wVersionRequested, &wsaData ); +} + +struct socket_creator_data { + SOCKET_TYPE *sock; + int port_num; +}; +/** + */ +static DWORD WINAPI +control_creator( LPVOID lpParameter) +{ + struct socket_creator_data *my_data = (struct socket_creator_data *)lpParameter; + + *my_data->sock = socket( PF_INET, SOCK_STREAM, 0); + + if ( *my_data->sock != INVALID_SOCKET) { + int connect_res; + struct sockaddr_in clientService; + + clientService.sin_family = AF_INET; + clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" ); + clientService.sin_port = htons( my_data->port_num); + + connect_res = connect( *my_data->sock, (SOCKADDR*) &clientService, sizeof(clientService)); + + if ( connect_res == SOCKET_ERROR) { + LOG_ERROR( "Failed to connect to socket\n"); + } + } + + return 0; +} + +static int INIT_PIPE(SOCKET_TYPE *pipefds) { + struct socket_creator_data temp_data = { + pipefds, + 24689 + }; + SOCKET_TYPE temp_sock = createSocket ( temp_data.port_num); + HANDLE temp_thread = INVALID_HANDLE_VALUE; + DWORD temp_threadID; + + res = -1; + + if ( temp_sock != -1) { + /* create a thread to connect to this socket */ + temp_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)control_creator, &temp_data, 0, &temp_threadID); + if ( temp_thread != INVALID_HANDLE_VALUE) { + struct sockaddr_in ignore_addr; + int addr_size = sizeof( ignore_addr); + + pipefds[1] = accept( temp_sock, (struct sockaddr *)&ignore_addr, &addr_size); + + if ( pipefds[1] != INVALID_SOCKET) { + if (set_socket_nodelay( pipefds+1) == 0) { + closesocket( temp_sock); + res = 0; + } + } + } + } + + if ( res != 0) { + LOG_ERROR( "Failed to create pipe\n"); + } + return res; +} +#else +#define INVALID_SOCKET -1 +#define RECV(A,B,C) read(A, (char*)B, C) +#define SEND(A,B,C) write(A, B, C) +#define CLOSESOCKET(A) close(A) +#define SOCKLEN_T socklen_t +static int set_socket_nodelay(int socket) { + int nodelay_opt = 1; + return setsockopt( socket, IPPROTO_TCP, TCP_NODELAY, + &nodelay_opt, sizeof( nodelay_opt)); +} +#define INIT_SOCKETS() 0 +static int INIT_PIPE(SOCKET_TYPE *pipefds) { + int res; + res = pipe( pipefds); + if ( res != 0) { + LOG_ERROR( "Failed to create pipe \"%s\"\n", strerror( errno)); + } + return res; +} +#endif + + #define LITTLE_ENDIAN_TO_UINT32_T( v) (\ ((v)[3] << 24) | \ ((v)[2] << 16) | \ @@ -1410,46 +1494,6 @@ static const armcpu_memory_iface gdb_memory_iface = { NULL }; - - - - -#ifdef WIN32 -struct socket_creator_data { - SOCKET_TYPE *sock; - int port_num; -}; -/** - */ -static DWORD WINAPI -control_creator( LPVOID lpParameter) -{ - struct socket_creator_data *my_data = (struct socket_creator_data *)lpParameter; - - *my_data->sock = socket( PF_INET, SOCK_STREAM, 0); - - if ( *my_data->sock != INVALID_SOCKET) { - int connect_res; - struct sockaddr_in clientService; - - clientService.sin_family = AF_INET; - clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" ); - clientService.sin_port = htons( my_data->port_num); - - connect_res = connect( *my_data->sock, (SOCKADDR*) &clientService, sizeof(clientService)); - - if ( connect_res == SOCKET_ERROR) { - LOG_ERROR( "Failed to connect to socket\n"); - } - } - - return 0; -} - -#endif - - - /** */ gdbstub_handle_t @@ -1492,69 +1536,8 @@ createStub_gdb( uint16_t port, stub->write_breakpoints = NULL; stub->access_breakpoints = NULL; -#ifdef WIN32 - /* initialise the winsock library */ - { - WORD wVersionRequested; - WSADATA wsaData; - int err; - - wVersionRequested = MAKEWORD( 2, 2 ); - - err = WSAStartup( wVersionRequested, &wsaData ); - if ( err != 0 ) { - return NULL; - } - } - - { - struct socket_creator_data temp_data = { - &stub->ctl_pipe[0], - 24689 - }; - SOCKET_TYPE temp_sock = createSocket ( temp_data.port_num); - HANDLE temp_thread = INVALID_HANDLE_VALUE; - DWORD temp_threadID; - - res = -1; - - if ( temp_sock != -1) { - /* create a thread to connect to this socket */ - temp_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)control_creator, &temp_data, 0, &temp_threadID); - if ( temp_thread != INVALID_HANDLE_VALUE) { - struct sockaddr_in ignore_addr; - int addr_size = sizeof( ignore_addr); - - stub->ctl_pipe[1] = accept( temp_sock, (struct sockaddr *)&ignore_addr, &addr_size); - - if ( stub->ctl_pipe[1] != INVALID_SOCKET) { - BOOL nodelay_opt = 1; - int set_res; - - /* make the socket NODELAY */ - set_res = setsockopt( stub->ctl_pipe[1], IPPROTO_TCP, TCP_NODELAY, - (char*)&nodelay_opt, sizeof( nodelay_opt)); - if ( set_res == 0) { - closesocket( temp_sock); - res = 0; - } - } - } - } - } - - if ( res != 0) { - LOG_ERROR( "Failed to create control socket\n"); - } -#else /* not WIN32 */ - /* create the control pipe */ - res = pipe( stub->ctl_pipe); - - if ( res != 0) { - LOG_ERROR( "Failed to create control pipe \"%s\"\n", strerror( errno)); - } -#endif - else { + if ( INIT_SOCKETS() != 0) return NULL; + if ( (res = INIT_PIPE(stub->ctl_pipe)) == 0) { stub->active = 1; stub->emu_stub_state = gdb_stub_state::RUNNING_EMU_GDB_STATE; stub->ctl_stub_state = gdb_stub_state::STOPPED_GDB_STATE; From d5693c54cf888d5ceeae047eff577b4330dd543a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 24 Oct 2021 04:10:24 +0000 Subject: [PATCH 4/6] cli frontend: fix 100% cpu when using gdb stub the emulator thread was consuming 100% cpu even when the debugger was active and execution paused. a second pipe was added to gdb stub, which allows communication in direction stub -> emulator/frontend, and also to infinitely block in the frontend until the debugger returns control, for example by typing "c" (continue) in gdb. the other frontends use an inefficient method of running usleep(1000) or similar in a loop, which will cause high cpu usage too, albeit not a full 100% but more like 10-20%. in order not to fill up the pipe with data for frontends that don't use this mechanism, the functionality needs to be explicitly enabled. (see functions added to gdbstub.h) the functions added could in theory also be used to communicate other data to the frontend, and optimally even replace all the locking between the 2 sides. --- desmume/src/frontend/posix/cli/main.cpp | 76 ++++++++++++++----------- desmume/src/gdbstub.h | 13 +++++ desmume/src/gdbstub/gdbstub.cpp | 32 ++++++++++- desmume/src/gdbstub/gdbstub_internal.h | 8 ++- 4 files changed, 93 insertions(+), 36 deletions(-) diff --git a/desmume/src/frontend/posix/cli/main.cpp b/desmume/src/frontend/posix/cli/main.cpp index d5d420d53..690d7c796 100644 --- a/desmume/src/frontend/posix/cli/main.cpp +++ b/desmume/src/frontend/posix/cli/main.cpp @@ -63,6 +63,21 @@ #ifdef GDB_STUB #include "../armcpu.h" #include "../gdbstub.h" +class CliDriver : public BaseDriver +{ +private: + gdbstub_handle_t __stubs[2]; +public: + virtual void EMU_DebugIdleUpdate() { + gdbstub_wait(__stubs); + } + virtual void setStubs(gdbstub_handle_t stubs[2]) { + this->__stubs[0] = stubs[0]; + this->__stubs[1] = stubs[1]; + } +}; +#else +class CliDriver : public BaseDriver {}; #endif volatile bool execute = false; @@ -478,6 +493,19 @@ static void desmume_cycle(struct ctrls_event_config * cfg) SPU_Emulate_user(); } +#ifdef GDB_STUB +static gdbstub_handle_t setup_gdb_stub(u16 port, armcpu_t *cpu, const armcpu_memory_iface *memio, const char* desc) { + gdbstub_handle_t stub = createStub_gdb(port, cpu, memio); + if ( stub == NULL) { + fprintf( stderr, "Failed to create %s gdbstub on port %d\n", desc, port); + exit( 1); + } else { + activateStub_gdb(stub); + } + return stub; +} +#endif + int main(int argc, char ** argv) { class configured_features my_config; struct ctrls_event_config ctrls_cfg; @@ -568,8 +596,8 @@ int main(int argc, char ** argv) { slot2_Init(); slot2_Change((NDS_SLOT2_TYPE)slot2_device_type); - driver = new BaseDriver(); - + driver = new CliDriver(); + #ifdef GDB_STUB gdbstub_mutex_init(); @@ -577,37 +605,22 @@ int main(int argc, char ** argv) { * Activate the GDB stubs * This has to come after NDS_Init() where the CPUs are set up. */ - gdbstub_handle_t arm9_gdb_stub = NULL; - gdbstub_handle_t arm7_gdb_stub = NULL; - + gdbstub_handle_t stubs[2] = {}; if ( my_config.arm9_gdb_port > 0) { - arm9_gdb_stub = createStub_gdb( my_config.arm9_gdb_port, + stubs[0] = setup_gdb_stub(my_config.arm9_gdb_port, &NDS_ARM9, - &arm9_direct_memory_iface); - - if ( arm9_gdb_stub == NULL) { - fprintf( stderr, "Failed to create ARM9 gdbstub on port %d\n", - my_config.arm9_gdb_port); - exit( 1); - } - else { - activateStub_gdb( arm9_gdb_stub); - } + &arm9_direct_memory_iface, "ARM9"); + } if ( my_config.arm7_gdb_port > 0) { - arm7_gdb_stub = createStub_gdb( my_config.arm7_gdb_port, + stubs[1] = setup_gdb_stub(my_config.arm7_gdb_port, &NDS_ARM7, - &arm7_base_memory_iface); - - if ( arm7_gdb_stub == NULL) { - fprintf( stderr, "Failed to create ARM7 gdbstub on port %d\n", - my_config.arm7_gdb_port); - exit( 1); - } - else { - activateStub_gdb( arm7_gdb_stub); - } + &arm7_base_memory_iface, "ARM7"); + } + ((CliDriver*)driver)->setStubs(stubs); + gdbstub_wait_set_enabled(stubs[0], 1); + gdbstub_wait_set_enabled(stubs[1], 1); #endif if ( !my_config.disable_sound) { @@ -795,15 +808,12 @@ int main(int argc, char ** argv) { uninit_joy(); #ifdef GDB_STUB - destroyStub_gdb( arm9_gdb_stub); - arm9_gdb_stub = NULL; - - destroyStub_gdb( arm7_gdb_stub); - arm7_gdb_stub = NULL; + destroyStub_gdb( stubs[0]); + destroyStub_gdb( stubs[1]); gdbstub_mutex_destroy(); #endif - + SDL_Quit(); NDS_DeInit(); diff --git a/desmume/src/gdbstub.h b/desmume/src/gdbstub.h index 84a29785b..02414d7f1 100644 --- a/desmume/src/gdbstub.h +++ b/desmume/src/gdbstub.h @@ -45,6 +45,19 @@ destroyStub_gdb( gdbstub_handle_t stub); void activateStub_gdb( gdbstub_handle_t stub); +/* wait until either of 2 gdb stubs gives control back to the emulator. + pass a stubs[2], one of them may be NULL. + return value: response from stub | (stub number<<31), + i.e. if stub 1 responded, the high bit is set (and so the result negative). + the primary usecase for this is to do a blocking wait until the stub returns + control to the emulator, in order to not waste cpu cycles. + returns 0 on failure or if no response was available. */ +int gdbstub_wait( gdbstub_handle_t *stubs); + +/* enable or disable use of the pipe for gdbstub_wait() */ +void gdbstub_wait_set_enabled(gdbstub_handle_t stub, int on); + + /* * An implementation of the following functions is required * for the GDB stub to function. diff --git a/desmume/src/gdbstub/gdbstub.cpp b/desmume/src/gdbstub/gdbstub.cpp index c05b1bd07..046f017fa 100644 --- a/desmume/src/gdbstub/gdbstub.cpp +++ b/desmume/src/gdbstub/gdbstub.cpp @@ -304,9 +304,35 @@ indicateCPUStop_gdb( struct gdb_stub_state *stub) { SEND( stub->ctl_pipe[1], &command, 1); } +int gdbstub_wait(gdbstub_handle_t *stubs) { + struct gdb_stub_state* g[2]; + g[0] = (struct gdb_stub_state *) stubs[0]; + g[1] = (struct gdb_stub_state *) stubs[1]; + fd_set set; + unsigned i; + FD_ZERO(&set); + for (i = 0; i < 2; ++i) + if(g[i]) FD_SET( g[i]->info_pipe[0], &set); + int res = select( FD_SETSIZE, &set, NULL, NULL, NULL); + if (res <= 0) return 0; + for (i = 0; i < 2; ++i) + if ( g[i] && FD_ISSET( g[i]->info_pipe[0], &set)) { + RECV( g[i]->info_pipe[0], &res, 4); + return res | (i << 31); + } + return 0; +} +void gdbstub_wait_set_enabled(gdbstub_handle_t stub, int on) { + struct gdb_stub_state* g = (struct gdb_stub_state *) stub; + if(g) g->info_pipe_enabled = on; +} - +static void infopipe_send(struct gdb_stub_state* g, int status) { + int resp = status; + if (g->info_pipe_enabled) + SEND(g->info_pipe[1], &resp, 4); +} /* * * @@ -701,6 +727,7 @@ processPacket_gdb( SOCKET_TYPE sock, const uint8_t *packet, /* remove the cpu stall */ stub->cpu_ctrl->unstall( stub->cpu_ctrl->data); NDS_debug_continue(); + infopipe_send(stub, 1); break; case 's': { @@ -725,6 +752,7 @@ processPacket_gdb( SOCKET_TYPE sock, const uint8_t *packet, stub->cpu_ctrl->unstall( stub->cpu_ctrl->data); //NDS_debug_step(); NDS_debug_continue(); + infopipe_send(stub, 2); break; } @@ -1537,7 +1565,7 @@ createStub_gdb( uint16_t port, stub->access_breakpoints = NULL; if ( INIT_SOCKETS() != 0) return NULL; - if ( (res = INIT_PIPE(stub->ctl_pipe)) == 0) { + if ( (res = INIT_PIPE(stub->ctl_pipe)) == 0 && INIT_PIPE(stub->info_pipe) == 0) { stub->active = 1; stub->emu_stub_state = gdb_stub_state::RUNNING_EMU_GDB_STATE; stub->ctl_stub_state = gdb_stub_state::STOPPED_GDB_STATE; diff --git a/desmume/src/gdbstub/gdbstub_internal.h b/desmume/src/gdbstub/gdbstub_internal.h index 0e6d2adf0..4d780a18f 100644 --- a/desmume/src/gdbstub/gdbstub_internal.h +++ b/desmume/src/gdbstub/gdbstub_internal.h @@ -171,8 +171,14 @@ struct gdb_stub_state { /** the free breakpoint descriptor list */ struct breakpoint_gdb *free_breakpoints; - /** the control pipe (or socket) to the gdb stub */ + /** the control pipe (or socket) to the gdb stub. this allows to send commands to the stub. */ SOCKET_TYPE ctl_pipe[2]; + + /** this pipe allows the stub to communicate to the controlling program. */ + SOCKET_TYPE info_pipe[2]; + + /** whether above pipe is enabled */ + int info_pipe_enabled; }; From ed5ff51c0ca7a6a65a6674d437d4c65fb7a41c17 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 24 Oct 2021 15:02:17 +0000 Subject: [PATCH 5/6] gdb stub: since debug info is printed anyway, show the actual command --- desmume/src/gdbstub/gdbstub.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desmume/src/gdbstub/gdbstub.cpp b/desmume/src/gdbstub/gdbstub.cpp index 046f017fa..6eebf0104 100644 --- a/desmume/src/gdbstub/gdbstub.cpp +++ b/desmume/src/gdbstub/gdbstub.cpp @@ -507,8 +507,8 @@ readPacket_gdb( SOCKET_TYPE sock, struct packet_reader_gdb *packet) { case MID_PACKET_READ_STATE: if ( cur_byte == '#') { - //DEBUG_LOG( "\nAbout to get checksum\n"); packet->buffer[packet->pos_index] = '\0'; + DEBUG_LOG( "\nAbout to get checksum for %s\n", packet->buffer); packet->state = FIRST_CHECKSUM_READ_STATE; } else if ( packet->pos_index >= BUFMAX - 1) { From 339ea16b15503d28cda09f74c633a316450ab2f3 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 24 Oct 2021 15:29:59 +0000 Subject: [PATCH 6/6] gdbstub_wait(): add timeout parameter this is probably helpful for frontends other than cli that have to repaint and react on events in the user interface, so they can set a timeout like 100 ms, or simply poll whether the stub is active using timeout 0. --- desmume/src/frontend/posix/cli/main.cpp | 2 +- desmume/src/gdbstub.h | 9 ++++++--- desmume/src/gdbstub/gdbstub.cpp | 10 ++++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/desmume/src/frontend/posix/cli/main.cpp b/desmume/src/frontend/posix/cli/main.cpp index 690d7c796..c47213c3b 100644 --- a/desmume/src/frontend/posix/cli/main.cpp +++ b/desmume/src/frontend/posix/cli/main.cpp @@ -69,7 +69,7 @@ private: gdbstub_handle_t __stubs[2]; public: virtual void EMU_DebugIdleUpdate() { - gdbstub_wait(__stubs); + gdbstub_wait(__stubs, -1L); } virtual void setStubs(gdbstub_handle_t stubs[2]) { this->__stubs[0] = stubs[0]; diff --git a/desmume/src/gdbstub.h b/desmume/src/gdbstub.h index 02414d7f1..99412606d 100644 --- a/desmume/src/gdbstub.h +++ b/desmume/src/gdbstub.h @@ -47,12 +47,15 @@ activateStub_gdb( gdbstub_handle_t stub); /* wait until either of 2 gdb stubs gives control back to the emulator. pass a stubs[2], one of them may be NULL. - return value: response from stub | (stub number<<31), - i.e. if stub 1 responded, the high bit is set (and so the result negative). the primary usecase for this is to do a blocking wait until the stub returns control to the emulator, in order to not waste cpu cycles. + the timeout parameter specifies the maximum time in milliseconds to wait. + if timeout == -1L, block indefinitely, if timeout == 0 return immediately, + effecting a poll. + return value: response from stub | (stub number<<31), + i.e. if stub 1 responded, the high bit is set (and so the result negative). returns 0 on failure or if no response was available. */ -int gdbstub_wait( gdbstub_handle_t *stubs); +int gdbstub_wait( gdbstub_handle_t *stubs, long timeout); /* enable or disable use of the pipe for gdbstub_wait() */ void gdbstub_wait_set_enabled(gdbstub_handle_t stub, int on); diff --git a/desmume/src/gdbstub/gdbstub.cpp b/desmume/src/gdbstub/gdbstub.cpp index 6eebf0104..cc8cbdd87 100644 --- a/desmume/src/gdbstub/gdbstub.cpp +++ b/desmume/src/gdbstub/gdbstub.cpp @@ -304,16 +304,22 @@ indicateCPUStop_gdb( struct gdb_stub_state *stub) { SEND( stub->ctl_pipe[1], &command, 1); } -int gdbstub_wait(gdbstub_handle_t *stubs) { +int gdbstub_wait(gdbstub_handle_t *stubs, long timeout) { struct gdb_stub_state* g[2]; g[0] = (struct gdb_stub_state *) stubs[0]; g[1] = (struct gdb_stub_state *) stubs[1]; fd_set set; unsigned i; + struct timeval tv = {}, *tvp = &tv; FD_ZERO(&set); for (i = 0; i < 2; ++i) if(g[i]) FD_SET( g[i]->info_pipe[0], &set); - int res = select( FD_SETSIZE, &set, NULL, NULL, NULL); + if (timeout == -1L) tvp = NULL; + else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + } + int res = select( FD_SETSIZE, &set, NULL, NULL, tvp); if (res <= 0) return 0; for (i = 0; i < 2; ++i) if ( g[i] && FD_ISSET( g[i]->info_pipe[0], &set)) {