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.
This commit is contained in:
parent
dbc1f06662
commit
d5693c54cf
|
@ -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();
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue