[source debug] refine some code in source debugging (#856)

- move the wait_cond from exec_env to debug_instance, so the debug thread can be waken up by any threads
- process more general query message from debugger
- refine debug instance create/destroy mechanism
- avoid creating debug instance during module instantiating
- avoid blocking execution thread during creating debug instance
- update related documents
This commit is contained in:
Xu Jun
2021-12-06 10:25:38 +08:00
committed by GitHub
parent c8fe1004aa
commit 2af5ae5abb
18 changed files with 401 additions and 145 deletions

View File

@ -7,7 +7,7 @@
#include "bh_log.h"
#include "gdbserver.h"
#include "platform_api_extension.h"
#include "bh_platform.h"
#include "wasm_interp.h"
#include "wasm_opcode.h"
#include "wasm_runtime.h"
@ -54,20 +54,11 @@ control_thread_routine(void *arg)
{
WASMDebugInstance *debug_inst = (WASMDebugInstance *)arg;
WASMDebugControlThread *control_thread = NULL;
WASMCluster *cluster = NULL;
WASMExecEnv *exec_env;
bh_assert(debug_inst);
control_thread = debug_inst->control_thread;
bh_assert(control_thread);
cluster = debug_inst->cluster;
bh_assert(cluster);
exec_env = bh_list_first_elem(&cluster->exec_env_list);
bh_assert(exec_env);
os_mutex_lock(&exec_env->wait_lock);
os_mutex_lock(&debug_inst->wait_lock);
control_thread->status = RUNNING;
@ -84,19 +75,31 @@ control_thread_routine(void *arg)
LOG_WARNING("control thread of debug object %p start\n", debug_inst);
control_thread->server =
wasm_launch_gdbserver(control_thread->ip_addr, control_thread->port);
wasm_create_gdbserver(control_thread->ip_addr, &control_thread->port);
if (!control_thread->server) {
LOG_ERROR("Failed to create debug server\n");
os_cond_signal(&exec_env->wait_cond);
os_mutex_unlock(&exec_env->wait_lock);
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&debug_inst->wait_lock);
return NULL;
}
control_thread->server->thread = control_thread;
/* control thread ready, notify main thread */
os_cond_signal(&exec_env->wait_cond);
os_mutex_unlock(&exec_env->wait_lock);
/*
* wasm gdbserver created, the execution thread
* doesn't need to wait for the debugger connection,
* so we wake up the execution thread before listen
*/
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&debug_inst->wait_lock);
/* wait lldb client to connect */
if (!wasm_gdbserver_listen(control_thread->server)) {
LOG_ERROR("Failed while connecting debugger\n");
wasm_runtime_free(control_thread->server);
return NULL;
}
while (true) {
os_mutex_lock(&control_thread->wait_lock);
@ -112,7 +115,7 @@ control_thread_routine(void *arg)
os_mutex_unlock(&control_thread->wait_lock);
}
LOG_VERBOSE("control thread of debug object %p stop\n", debug_inst);
LOG_VERBOSE("control thread of debug object [%p] stopped\n", debug_inst);
return NULL;
}
@ -120,12 +123,6 @@ static WASMDebugControlThread *
wasm_debug_control_thread_create(WASMDebugInstance *debug_instance)
{
WASMDebugControlThread *control_thread;
WASMCluster *cluster = debug_instance->cluster;
WASMExecEnv *exec_env;
bh_assert(cluster);
exec_env = bh_list_first_elem(&cluster->exec_env_list);
bh_assert(exec_env);
if (!(control_thread =
wasm_runtime_malloc(sizeof(WASMDebugControlThread)))) {
@ -139,18 +136,18 @@ wasm_debug_control_thread_create(WASMDebugInstance *debug_instance)
debug_instance->control_thread = control_thread;
os_mutex_lock(&exec_env->wait_lock);
os_mutex_lock(&debug_instance->wait_lock);
if (0
!= os_thread_create(&control_thread->tid, control_thread_routine,
debug_instance, APP_THREAD_STACK_SIZE_MAX)) {
os_mutex_unlock(&control_thread->wait_lock);
os_mutex_unlock(&debug_instance->wait_lock);
goto fail1;
}
/* wait until the debug control thread ready */
os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock);
os_mutex_unlock(&exec_env->wait_lock);
os_cond_wait(&debug_instance->wait_cond, &debug_instance->wait_lock);
os_mutex_unlock(&debug_instance->wait_lock);
if (!control_thread->server)
goto fail1;
@ -174,7 +171,8 @@ static void
wasm_debug_control_thread_destroy(WASMDebugInstance *debug_instance)
{
WASMDebugControlThread *control_thread = debug_instance->control_thread;
LOG_VERBOSE("control thread of debug object %p stop\n", debug_instance);
LOG_VERBOSE("stopping control thread of debug object [%p]\n",
debug_instance);
control_thread->status = STOPPED;
os_mutex_lock(&control_thread->wait_lock);
wasm_close_gdbserver(control_thread->server);
@ -286,6 +284,15 @@ wasm_debug_instance_create(WASMCluster *cluster)
return NULL;
}
memset(instance, 0, sizeof(WASMDebugInstance));
if (os_mutex_init(&instance->wait_lock) != 0) {
goto fail1;
}
if (os_cond_init(&instance->wait_cond) != 0) {
goto fail2;
}
bh_list_init(&instance->break_point_list);
instance->cluster = cluster;
@ -297,29 +304,21 @@ wasm_debug_instance_create(WASMCluster *cluster)
if (!wasm_debug_control_thread_create(instance)) {
LOG_ERROR("WASM Debug Engine error: failed to create control thread");
wasm_runtime_free(instance);
return NULL;
goto fail3;
}
return instance;
}
static WASMDebugInstance *
wasm_cluster_get_debug_instance(WASMDebugEngine *engine, WASMCluster *cluster)
{
WASMDebugInstance *instance;
os_mutex_lock(&g_debug_engine->instance_list_lock);
instance = bh_list_first_elem(&engine->debug_instance_list);
while (instance) {
if (instance->cluster == cluster) {
os_mutex_unlock(&g_debug_engine->instance_list_lock);
return instance;
}
instance = bh_list_elem_next(instance);
}
os_mutex_unlock(&g_debug_engine->instance_list_lock);
wasm_cluster_set_debug_inst(cluster, instance);
return instance;
fail3:
os_cond_destroy(&instance->wait_cond);
fail2:
os_mutex_destroy(&instance->wait_lock);
fail1:
wasm_runtime_free(instance);
return NULL;
}
static void
@ -347,7 +346,7 @@ wasm_debug_instance_destroy(WASMCluster *cluster)
return;
}
instance = wasm_cluster_get_debug_instance(g_debug_engine, cluster);
instance = cluster->debug_inst;
if (instance) {
/* destroy control thread */
wasm_debug_control_thread_destroy(instance);
@ -359,7 +358,11 @@ wasm_debug_instance_destroy(WASMCluster *cluster)
/* destroy all breakpoints */
wasm_debug_instance_destroy_breakpoints(instance);
os_mutex_destroy(&instance->wait_lock);
os_cond_destroy(&instance->wait_cond);
wasm_runtime_free(instance);
cluster->debug_inst = NULL;
}
}
@ -434,47 +437,78 @@ wasm_debug_instance_get_tids(WASMDebugInstance *instance, uint64 tids[],
int len)
{
WASMExecEnv *exec_env;
int i = 0;
int i = 0, threads_num = 0;
if (!instance)
return 0;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env && i < len) {
tids[i++] = exec_env->handle;
/* Some threads may not be ready */
if (exec_env->handle != 0) {
tids[i++] = exec_env->handle;
threads_num++;
}
exec_env = bh_list_elem_next(exec_env);
}
LOG_VERBOSE("find %d tids\n", i);
return i;
LOG_VERBOSE("find %d tids\n", threads_num);
return threads_num;
}
static WASMExecEnv *
get_stopped_thread(WASMCluster *cluster)
{
WASMExecEnv *exec_env;
exec_env = bh_list_first_elem(&cluster->exec_env_list);
while (exec_env) {
if (exec_env->current_status->running_status != STATUS_RUNNING) {
return exec_env;
}
exec_env = bh_list_elem_next(exec_env);
}
return NULL;
}
uint64
wasm_debug_instance_wait_thread(WASMDebugInstance *instance, uint64 tid,
uint32 *status)
{
WASMExecEnv *exec_env;
WASMExecEnv *last_exec_env = NULL;
WASMExecEnv *exec_env = NULL;
os_mutex_lock(&instance->wait_lock);
while ((instance->cluster->exec_env_list.len != 0)
&& ((exec_env = get_stopped_thread(instance->cluster)) == NULL)) {
os_cond_wait(&instance->wait_cond, &instance->wait_lock);
}
os_mutex_unlock(&instance->wait_lock);
/* If cluster has no exec_env, then this whole cluster is exiting */
if (instance->cluster->exec_env_list.len == 0) {
*status = 0;
return 0;
}
instance->current_tid = exec_env->handle;
*status = exec_env->current_status->signal_flag;
return exec_env->handle;
}
uint32
wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, uint64 tid)
{
WASMExecEnv *exec_env = NULL;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
last_exec_env = exec_env;
if (instance->current_tid != 0
&& last_exec_env->handle == instance->current_tid) {
break;
if (exec_env->handle == tid) {
return exec_env->current_status->signal_flag;
}
exec_env = bh_list_elem_next(exec_env);
}
if (last_exec_env) {
wasm_cluster_wait_thread_status(last_exec_env, status);
if (instance->current_tid == 0)
instance->current_tid = last_exec_env->handle;
return last_exec_env->handle;
}
else {
*status = ~0;
return 0;
}
return 0;
}
void

View File

@ -42,6 +42,8 @@ typedef struct WASMDebugInstance {
WASMCluster *cluster;
uint32 id;
korp_tid current_tid;
korp_mutex wait_lock;
korp_cond wait_cond;
} WASMDebugInstance;
typedef enum WASMDebugEventKind {
@ -160,6 +162,9 @@ uint64
wasm_debug_instance_wait_thread(WASMDebugInstance *instance, uint64 tid,
uint32 *status);
uint32
wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, uint64 tid);
bool
wasm_debug_instance_singlestep(WASMDebugInstance *instance, uint64 tid);

View File

@ -51,17 +51,18 @@ static struct packet_handler_elem packet_handler_table[255] = {
};
WASMGDBServer *
wasm_launch_gdbserver(char *host, int port)
wasm_create_gdbserver(char *host, int *port)
{
int listen_fd = -1;
const int one = 1;
struct sockaddr_in addr;
socklen_t socklen;
int ret;
int sockt_fd = 0;
WASMGDBServer *server;
bh_assert(port);
if (!(server = wasm_runtime_malloc(sizeof(WASMGDBServer)))) {
LOG_ERROR("wasm gdb server error: failed to allocate memory");
return NULL;
@ -90,7 +91,7 @@ wasm_launch_gdbserver(char *host, int port)
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(host);
addr.sin_port = htons(port);
addr.sin_port = htons(*port);
ret = bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
@ -103,25 +104,12 @@ wasm_launch_gdbserver(char *host, int port)
LOG_ERROR("%s", strerror(errno));
goto fail;
}
LOG_WARNING("Debug server listening on %s:%d\n", host,
ntohs(addr.sin_port));
LOG_WARNING("Listening on %s:%d\n", host, ntohs(addr.sin_port));
ret = listen(listen_fd, 1);
if (ret < 0) {
LOG_ERROR("wasm gdb server error: listen() failed");
goto fail;
}
*port = ntohs(addr.sin_port);
server->listen_fd = listen_fd;
sockt_fd = accept(listen_fd, NULL, NULL);
if (sockt_fd < 0) {
LOG_ERROR("wasm gdb server error: accept() failed");
goto fail;
}
LOG_VERBOSE("accept gdb client");
server->socket_fd = sockt_fd;
server->noack = false;
return server;
fail:
@ -134,6 +122,35 @@ fail:
return NULL;
}
bool
wasm_gdbserver_listen(WASMGDBServer *server)
{
int ret;
int sockt_fd = 0;
ret = listen(server->listen_fd, 1);
if (ret < 0) {
LOG_ERROR("wasm gdb server error: listen() failed");
goto fail;
}
sockt_fd = accept(server->listen_fd, NULL, NULL);
if (sockt_fd < 0) {
LOG_ERROR("wasm gdb server error: accept() failed");
goto fail;
}
LOG_VERBOSE("accept gdb client");
server->socket_fd = sockt_fd;
server->noack = false;
return true;
fail:
shutdown(server->listen_fd, SHUT_RDWR);
close(server->listen_fd);
return false;
}
void
wasm_close_gdbserver(WASMGDBServer *server)
{

View File

@ -33,7 +33,10 @@ typedef struct WASMGDBServer {
} WASMGDBServer;
WASMGDBServer *
wasm_launch_gdbserver(char *addr, int port);
wasm_create_gdbserver(char *addr, int *port);
bool
wasm_gdbserver_listen(WASMGDBServer *server);
void
wasm_close_gdbserver(WASMGDBServer *server);

View File

@ -17,6 +17,9 @@
#define MAX_PACKET_SIZE (0x20000)
static char tmpbuf[MAX_PACKET_SIZE];
static void
send_thread_stop_status(WASMGDBServer *server, uint32_t status, uint64_t tid);
void
handle_generay_set(WASMGDBServer *server, char *payload)
{
@ -261,6 +264,20 @@ handle_generay_query(WASMGDBServer *server, char *payload)
if (args && (!strcmp(name, "WasmGlobal"))) {
porcess_wasm_global(server, args);
}
if (!strcmp(name, "Offsets")) {
write_packet(server, "");
}
if (!strncmp(name, "ThreadStopInfo", strlen("ThreadStopInfo"))) {
int32 prefix_len = strlen("ThreadStopInfo");
uint64 tid = strtol(name + prefix_len, NULL, 16);
uint32 status = wasm_debug_instance_get_thread_status(
server->thread->debug_instance, tid);
send_thread_stop_status(server, status, tid);
}
}
static void
@ -332,19 +349,32 @@ handle_v_packet(WASMGDBServer *server, char *payload)
write_packet(server, "vCont;c;C;s;S;");
if (!strcmp("Cont", name)) {
if (args && args[0] == 's') {
char *numstring = strchr(args, ':');
if (numstring) {
*numstring++ = '\0';
uint64_t tid = strtol(numstring, NULL, 16);
wasm_debug_instance_set_cur_thread(
(WASMDebugInstance *)server->thread->debug_instance, tid);
wasm_debug_instance_singlestep(
(WASMDebugInstance *)server->thread->debug_instance, tid);
tid = wasm_debug_instance_wait_thread(
(WASMDebugInstance *)server->thread->debug_instance, tid,
&status);
send_thread_stop_status(server, status, tid);
if (args) {
if (args[0] == 's' || args[0] == 'c') {
char *numstring = strchr(args, ':');
if (numstring) {
*numstring++ = '\0';
uint64_t tid = strtol(numstring, NULL, 16);
wasm_debug_instance_set_cur_thread(
(WASMDebugInstance *)server->thread->debug_instance,
tid);
if (args[0] == 's') {
wasm_debug_instance_singlestep(
(WASMDebugInstance *)server->thread->debug_instance,
tid);
}
else {
wasm_debug_instance_continue(
(WASMDebugInstance *)
server->thread->debug_instance);
}
tid = wasm_debug_instance_wait_thread(
(WASMDebugInstance *)server->thread->debug_instance,
tid, &status);
send_thread_stop_status(server, status, tid);
}
}
}
}