Merge pull request #477 from rofl0r/gdbstub_block

Gdbstub improvement, fix 100% cpu use in cli frontend
This commit is contained in:
zeromus 2021-10-24 21:07:12 -04:00 committed by GitHub
commit b8bed7082b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 236 additions and 226 deletions

View File

@ -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<bool FORCE>
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<int PROCNUM> static void execHardware_interrupts_core()

View File

@ -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();

View File

@ -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.

View File

@ -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;

View File

@ -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;
};