Allow source debugger reconnection (#1667)
Allow to wait for a new debugger connection once the previous one is disconnected: - when receiving a detach command - when the client socket is closed (for example, lldb process is killed)
This commit is contained in:
parent
5b144c491d
commit
3b4033aceb
@ -55,9 +55,21 @@ allocate_instance_id()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
should_stop(WASMDebugControlThread *control_thread)
|
is_thread_running(WASMDebugControlThread *control_thread)
|
||||||
{
|
{
|
||||||
return control_thread->status != RUNNING;
|
return control_thread->status == RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_thread_stopped(WASMDebugControlThread *control_thread)
|
||||||
|
{
|
||||||
|
return control_thread->status == STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_thread_detached(WASMDebugControlThread *control_thread)
|
||||||
|
{
|
||||||
|
return control_thread->status == DETACHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
@ -109,73 +121,88 @@ control_thread_routine(void *arg)
|
|||||||
os_cond_signal(&debug_inst->wait_cond);
|
os_cond_signal(&debug_inst->wait_cond);
|
||||||
os_mutex_unlock(&debug_inst->wait_lock);
|
os_mutex_unlock(&debug_inst->wait_lock);
|
||||||
|
|
||||||
/* wait lldb client to connect */
|
|
||||||
if (!wasm_gdbserver_listen(control_thread->server)) {
|
if (!wasm_gdbserver_listen(control_thread->server)) {
|
||||||
LOG_ERROR("Failed while connecting debugger\n");
|
LOG_ERROR("Failed while listening for debugger\n");
|
||||||
wasm_runtime_free(control_thread->server);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* outer infinite loop: try to connect with the debugger */
|
||||||
while (true) {
|
while (true) {
|
||||||
os_mutex_lock(&control_thread->wait_lock);
|
/* wait lldb client to connect */
|
||||||
if (!should_stop(control_thread)) {
|
if (!wasm_gdbserver_accept(control_thread->server)) {
|
||||||
/* send thread stop reply */
|
LOG_ERROR("Failed while accepting debugger connection\n");
|
||||||
if (debug_inst->stopped_thread
|
return NULL;
|
||||||
&& debug_inst->current_state == APP_RUNNING) {
|
}
|
||||||
uint32 status;
|
|
||||||
korp_tid tid;
|
|
||||||
|
|
||||||
status =
|
control_thread->status = RUNNING;
|
||||||
(uint32)
|
|
||||||
debug_inst->stopped_thread->current_status->signal_flag;
|
|
||||||
tid = debug_inst->stopped_thread->handle;
|
|
||||||
|
|
||||||
if (debug_inst->stopped_thread->current_status->running_status
|
/* inner infinite loop: keep serving until detach */
|
||||||
== STATUS_EXIT) {
|
while (true) {
|
||||||
/* If the thread exits, report "W00" if it's the last thread
|
os_mutex_lock(&control_thread->wait_lock);
|
||||||
* in the cluster, otherwise ignore this event */
|
if (is_thread_running(control_thread)) {
|
||||||
status = 0;
|
/* send thread stop reply */
|
||||||
|
if (debug_inst->stopped_thread
|
||||||
|
&& debug_inst->current_state == APP_RUNNING) {
|
||||||
|
uint32 status;
|
||||||
|
korp_tid tid;
|
||||||
|
|
||||||
/* By design, all the other threads should have been stopped
|
status = (uint32)debug_inst->stopped_thread->current_status
|
||||||
* at this moment, so it is safe to access the
|
->signal_flag;
|
||||||
* exec_env_list.len without lock */
|
tid = debug_inst->stopped_thread->handle;
|
||||||
if (debug_inst->cluster->exec_env_list.len != 1) {
|
|
||||||
debug_inst->stopped_thread = NULL;
|
if (debug_inst->stopped_thread->current_status
|
||||||
|
->running_status
|
||||||
|
== STATUS_EXIT) {
|
||||||
|
/* If the thread exits, report "W00" if it's the last
|
||||||
|
* thread in the cluster, otherwise ignore this event */
|
||||||
|
status = 0;
|
||||||
|
|
||||||
|
/* By design, all the other threads should have been
|
||||||
|
* stopped at this moment, so it is safe to access the
|
||||||
|
* exec_env_list.len without lock */
|
||||||
|
if (debug_inst->cluster->exec_env_list.len != 1) {
|
||||||
|
debug_inst->stopped_thread = NULL;
|
||||||
|
/* The exiting thread may wait for the signal */
|
||||||
|
os_cond_signal(&debug_inst->wait_cond);
|
||||||
|
os_mutex_unlock(&control_thread->wait_lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_debug_instance_set_cur_thread(
|
||||||
|
debug_inst, debug_inst->stopped_thread->handle);
|
||||||
|
|
||||||
|
send_thread_stop_status(control_thread->server, status,
|
||||||
|
tid);
|
||||||
|
|
||||||
|
debug_inst->current_state = APP_STOPPED;
|
||||||
|
debug_inst->stopped_thread = NULL;
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
/* The exiting thread may wait for the signal */
|
/* The exiting thread may wait for the signal */
|
||||||
os_cond_signal(&debug_inst->wait_cond);
|
os_cond_signal(&debug_inst->wait_cond);
|
||||||
os_mutex_unlock(&control_thread->wait_lock);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_debug_instance_set_cur_thread(
|
/* Processing incoming requests */
|
||||||
debug_inst, debug_inst->stopped_thread->handle);
|
if (!wasm_gdbserver_handle_packet(control_thread->server)) {
|
||||||
|
control_thread->status = STOPPED;
|
||||||
send_thread_stop_status(control_thread->server, status, tid);
|
LOG_VERBOSE("control thread of debug object [%p] stopped\n",
|
||||||
|
debug_inst);
|
||||||
debug_inst->current_state = APP_STOPPED;
|
wasm_close_gdbserver(control_thread->server);
|
||||||
debug_inst->stopped_thread = NULL;
|
|
||||||
|
|
||||||
if (status == 0) {
|
|
||||||
/* The exiting thread may wait for the signal */
|
|
||||||
os_cond_signal(&debug_inst->wait_cond);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (is_thread_detached(control_thread)) {
|
||||||
/* Processing incoming requests */
|
os_mutex_unlock(&control_thread->wait_lock);
|
||||||
if (!wasm_gdbserver_handle_packet(control_thread->server)) {
|
break;
|
||||||
control_thread->status = STOPPED;
|
}
|
||||||
|
else if (is_thread_stopped(control_thread)) {
|
||||||
|
os_mutex_unlock(&control_thread->wait_lock);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
os_mutex_unlock(&control_thread->wait_lock);
|
os_mutex_unlock(&control_thread->wait_lock);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
os_mutex_unlock(&control_thread->wait_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERBOSE("control thread of debug object [%p] stopped\n", debug_inst);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static WASMDebugControlThread *
|
static WASMDebugControlThread *
|
||||||
@ -987,6 +1014,38 @@ wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
wasm_debug_instance_detach(WASMDebugInstance *instance)
|
||||||
|
{
|
||||||
|
WASMExecEnv *exec_env;
|
||||||
|
|
||||||
|
if (!instance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
|
||||||
|
if (!exec_env)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wasm_gdbserver_detach(instance->control_thread->server);
|
||||||
|
|
||||||
|
while (exec_env) {
|
||||||
|
if (instance->current_state == APP_STOPPED) {
|
||||||
|
/* Resume all threads since remote debugger detached*/
|
||||||
|
os_mutex_lock(&exec_env->wait_lock);
|
||||||
|
exec_env->current_status->running_status = STATUS_RUNNING;
|
||||||
|
os_cond_signal(&exec_env->wait_cond);
|
||||||
|
os_mutex_unlock(&exec_env->wait_lock);
|
||||||
|
}
|
||||||
|
exec_env = bh_list_elem_next(exec_env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* relaunch, accept new debug connection */
|
||||||
|
instance->current_state = DBG_LAUNCHING;
|
||||||
|
instance->control_thread->status = DETACHED;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wasm_debug_instance_kill(WASMDebugInstance *instance)
|
wasm_debug_instance_kill(WASMDebugInstance *instance)
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
typedef enum WASMDebugControlThreadStatus {
|
typedef enum WASMDebugControlThreadStatus {
|
||||||
RUNNING,
|
RUNNING,
|
||||||
|
DETACHED,
|
||||||
STOPPED,
|
STOPPED,
|
||||||
} WASMDebugControlThreadStatus;
|
} WASMDebugControlThreadStatus;
|
||||||
|
|
||||||
@ -185,6 +186,9 @@ wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance);
|
|||||||
bool
|
bool
|
||||||
wasm_debug_instance_continue(WASMDebugInstance *instance);
|
wasm_debug_instance_continue(WASMDebugInstance *instance);
|
||||||
|
|
||||||
|
bool
|
||||||
|
wasm_debug_instance_detach(WASMDebugInstance *instance);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wasm_debug_instance_kill(WASMDebugInstance *instance);
|
wasm_debug_instance_kill(WASMDebugInstance *instance);
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ static const struct packet_handler_elem packet_handler_table[255] = {
|
|||||||
DEL_HANDLER('c', handle_continue_request),
|
DEL_HANDLER('c', handle_continue_request),
|
||||||
DEL_HANDLER('k', handle_kill_request),
|
DEL_HANDLER('k', handle_kill_request),
|
||||||
DEL_HANDLER('_', handle____request),
|
DEL_HANDLER('_', handle____request),
|
||||||
|
DEL_HANDLER('D', handle_detach_request),
|
||||||
};
|
};
|
||||||
|
|
||||||
WASMGDBServer *
|
WASMGDBServer *
|
||||||
@ -89,7 +90,6 @@ fail:
|
|||||||
bool
|
bool
|
||||||
wasm_gdbserver_listen(WASMGDBServer *server)
|
wasm_gdbserver_listen(WASMGDBServer *server)
|
||||||
{
|
{
|
||||||
bh_socket_t sockt_fd = (bh_socket_t)-1;
|
|
||||||
int32 ret;
|
int32 ret;
|
||||||
|
|
||||||
ret = os_socket_listen(server->listen_fd, 1);
|
ret = os_socket_listen(server->listen_fd, 1);
|
||||||
@ -98,6 +98,23 @@ wasm_gdbserver_listen(WASMGDBServer *server)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_VERBOSE("listen for gdb client");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
os_socket_shutdown(server->listen_fd);
|
||||||
|
os_socket_close(server->listen_fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
wasm_gdbserver_accept(WASMGDBServer *server)
|
||||||
|
{
|
||||||
|
|
||||||
|
bh_socket_t sockt_fd = (bh_socket_t)-1;
|
||||||
|
|
||||||
|
LOG_VERBOSE("waiting for gdb client to connect...");
|
||||||
|
|
||||||
os_socket_accept(server->listen_fd, &sockt_fd, NULL, NULL);
|
os_socket_accept(server->listen_fd, &sockt_fd, NULL, NULL);
|
||||||
if (sockt_fd < 0) {
|
if (sockt_fd < 0) {
|
||||||
LOG_ERROR("wasm gdb server error: socket accept failed");
|
LOG_ERROR("wasm gdb server error: socket accept failed");
|
||||||
@ -115,6 +132,15 @@ fail:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wasm_gdbserver_detach(WASMGDBServer *server)
|
||||||
|
{
|
||||||
|
if (server->socket_fd > 0) {
|
||||||
|
os_socket_shutdown(server->socket_fd);
|
||||||
|
os_socket_close(server->socket_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
wasm_close_gdbserver(WASMGDBServer *server)
|
wasm_close_gdbserver(WASMGDBServer *server)
|
||||||
{
|
{
|
||||||
@ -263,8 +289,9 @@ wasm_gdbserver_handle_packet(WASMGDBServer *server)
|
|||||||
n = os_socket_recv(server->socket_fd, buf, sizeof(buf));
|
n = os_socket_recv(server->socket_fd, buf, sizeof(buf));
|
||||||
|
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
LOG_VERBOSE("Debugger disconnected");
|
handle_detach_request(server, NULL);
|
||||||
return false;
|
LOG_VERBOSE("Debugger disconnected, waiting for debugger reconnection");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (n < 0) {
|
else if (n < 0) {
|
||||||
#if defined(BH_PLATFORM_WINDOWS)
|
#if defined(BH_PLATFORM_WINDOWS)
|
||||||
|
@ -56,6 +56,12 @@ wasm_create_gdbserver(const char *host, int32 *port);
|
|||||||
bool
|
bool
|
||||||
wasm_gdbserver_listen(WASMGDBServer *server);
|
wasm_gdbserver_listen(WASMGDBServer *server);
|
||||||
|
|
||||||
|
bool
|
||||||
|
wasm_gdbserver_accept(WASMGDBServer *server);
|
||||||
|
|
||||||
|
void
|
||||||
|
wasm_gdbserver_detach(WASMGDBServer *server);
|
||||||
|
|
||||||
void
|
void
|
||||||
wasm_close_gdbserver(WASMGDBServer *server);
|
wasm_close_gdbserver(WASMGDBServer *server);
|
||||||
|
|
||||||
|
@ -777,3 +777,13 @@ handle____request(WASMGDBServer *server, char *payload)
|
|||||||
handle_free(server, args);
|
handle_free(server, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
handle_detach_request(WASMGDBServer *server, char *payload)
|
||||||
|
{
|
||||||
|
if (payload != NULL) {
|
||||||
|
write_packet(server, "OK");
|
||||||
|
}
|
||||||
|
wasm_debug_instance_detach(
|
||||||
|
(WASMDebugInstance *)server->thread->debug_instance);
|
||||||
|
}
|
||||||
|
@ -62,6 +62,9 @@ handle_kill_request(WASMGDBServer *server, char *payload);
|
|||||||
void
|
void
|
||||||
handle____request(WASMGDBServer *server, char *payload);
|
handle____request(WASMGDBServer *server, char *payload);
|
||||||
|
|
||||||
|
void
|
||||||
|
handle_detach_request(WASMGDBServer *server, char *payload);
|
||||||
|
|
||||||
void
|
void
|
||||||
send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid);
|
send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid);
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user