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() diff --git a/desmume/src/frontend/posix/cli/main.cpp b/desmume/src/frontend/posix/cli/main.cpp index d5d420d53..c47213c3b 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, -1L); + } + 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..99412606d 100644 --- a/desmume/src/gdbstub.h +++ b/desmume/src/gdbstub.h @@ -45,6 +45,22 @@ 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. + 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, long timeout); + +/* 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 2f7751644..cc8cbdd87 100644 --- a/desmume/src/gdbstub/gdbstub.cpp +++ b/desmume/src/gdbstub/gdbstub.cpp @@ -48,6 +48,7 @@ #endif #endif // HOST_WINDOWS + slock *cpu_mutex = NULL; #ifdef __GNUC__ @@ -68,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) | \ @@ -187,28 +295,50 @@ 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); } +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); + 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)) { + 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); +} /* * * @@ -383,8 +513,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) { @@ -603,6 +733,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': { @@ -627,6 +758,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; } @@ -974,29 +1106,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 +1203,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 +1247,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 +1260,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 +1279,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 +1299,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 +1353,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 +1369,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; } @@ -1446,46 +1528,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 @@ -1528,69 +1570,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 - /* 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 && 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; };