[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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 401 additions and 145 deletions

View File

@ -148,9 +148,6 @@ wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst,
wasm_exec_env_destroy_internal(exec_env);
return NULL;
}
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_debug_instance_create(cluster);
#endif
#endif /* end of WASM_ENABLE_THREAD_MGR */
return exec_env;
@ -165,7 +162,6 @@ wasm_exec_env_destroy(WASMExecEnv *exec_env)
if (cluster) {
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_cluster_thread_exited(exec_env);
wasm_debug_instance_destroy(cluster);
#endif
wasm_cluster_terminate_all_except_self(cluster, exec_env);
wasm_cluster_del_exec_env(cluster, exec_env);

View File

@ -294,6 +294,26 @@ get_package_type(const uint8 *buf, uint32 size)
return Package_Type_Unknown;
}
#if (WASM_ENABLE_THREAD_MGR != 0) && (WASM_ENABLE_DEBUG_INTERP != 0)
uint32
wasm_runtime_start_debug_instance(WASMExecEnv *exec_env)
{
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster);
if (cluster->debug_inst) {
LOG_WARNING("Cluster already bind to a debug instance");
return cluster->debug_inst->control_thread->port;
}
if (wasm_debug_instance_create(cluster)) {
return cluster->debug_inst->control_thread->port;
}
return 0;
}
#endif
#if WASM_ENABLE_MULTI_MODULE != 0
static module_reader reader;
static module_destroyer destroyer;
@ -1431,7 +1451,7 @@ wasm_runtime_create_exec_env_and_call_wasm(
if (module_inst->module_type == Wasm_Module_Bytecode)
ret = wasm_create_exec_env_and_call_function(
(WASMModuleInstance *)module_inst, (WASMFunctionInstance *)function,
argc, argv);
argc, argv, true);
#endif
#if WASM_ENABLE_AOT != 0
if (module_inst->module_type == Wasm_Module_AoT)

View File

@ -494,6 +494,12 @@ wasm_runtime_call_wasm_v(WASMExecEnv *exec_env,
uint32 num_results, wasm_val_t *results,
uint32 num_args, ...);
#if WASM_ENABLE_DEBUG_INTERP != 0
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN uint32
wasm_runtime_start_debug_instance(WASMExecEnv *exec_env);
#endif
/**
* Call a function reference of a given WASM runtime instance with
* arguments.

View File

@ -408,6 +408,26 @@ wasm_runtime_create_exec_env(wasm_module_inst_t module_inst,
WASM_RUNTIME_API_EXTERN void
wasm_runtime_destroy_exec_env(wasm_exec_env_t exec_env);
/**
* Start debug instance based on given execution environment.
* Note:
* The debug instance will be destroyed during destroying the
* execution environment, developers don't need to destroy it
* manually.
* If the cluster of this execution environment has already
* been bound to a debug instance, this function will return true
* directly.
* If developer spawns some exec_env by wasm_runtime_spawn_exec_env,
* don't need to call this function for every spawned exec_env as
* they are sharing the same cluster with the main exec_env.
*
* @param exec_env the execution environment to start debug instance
*
* @return debug port if success, 0 otherwise.
*/
WASM_RUNTIME_API_EXTERN uint32_t
wasm_runtime_start_debug_instance(wasm_exec_env_t exec_env);
/**
* Initialize thread environment.
* Note:

View File

@ -419,6 +419,20 @@ struct WASMModule {
uint64 buf_code_size;
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
/**
* Count how many instances reference this module. When source
* debugging feature enabled, the debugger may modify the code
* section of the module, so we need to report a warning if user
* create several instances based on the same module
*
* Sub_instances created by lib-pthread or spawn API will not
* influence or check the ref count
*/
uint32 ref_count;
korp_mutex ref_count_lock;
#endif
#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0
const uint8 *name_section_buf;
const uint8 *name_section_buf_end;

View File

@ -3237,6 +3237,10 @@ create_module(char *error_buf, uint32 error_buf_size)
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
bh_list_init(&module->fast_opcode_list);
if (os_mutex_init(&module->ref_count_lock) != 0) {
wasm_runtime_free(module);
return NULL;
}
#endif
return module;
}
@ -3568,6 +3572,7 @@ wasm_loader_unload(WASMModule *module)
wasm_runtime_free(fast_opcode);
fast_opcode = next;
}
os_mutex_destroy(&module->ref_count_lock);
#endif
wasm_runtime_free(module);
}

View File

@ -900,7 +900,7 @@ execute_post_inst_function(WASMModuleInstance *module_inst)
return true;
return wasm_create_exec_env_and_call_function(module_inst, post_inst_func,
0, NULL);
0, NULL, false);
}
#if WASM_ENABLE_BULK_MEMORY != 0
@ -929,7 +929,7 @@ execute_memory_init_function(WASMModuleInstance *module_inst)
return true;
return wasm_create_exec_env_and_call_function(module_inst, memory_init_func,
0, NULL);
0, NULL, false);
}
#endif
@ -944,7 +944,8 @@ execute_start_function(WASMModuleInstance *module_inst)
bh_assert(!func->is_import_func && func->param_cell_num == 0
&& func->ret_cell_num == 0);
return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL);
return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL,
false);
}
static bool
@ -972,11 +973,11 @@ execute_malloc_function(WASMModuleInstance *module_inst,
}
ret = wasm_create_exec_env_and_call_function(module_inst, malloc_func, argc,
argv);
argv, false);
if (retain_func && ret) {
ret = wasm_create_exec_env_and_call_function(module_inst, retain_func,
1, argv);
1, argv, false);
}
if (ret)
@ -992,7 +993,7 @@ execute_free_function(WASMModuleInstance *module_inst,
argv[0] = offset;
return wasm_create_exec_env_and_call_function(module_inst, free_func, 1,
argv);
argv, false);
}
#if WASM_ENABLE_MULTI_MODULE != 0
@ -1125,6 +1126,19 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size,
if (!module)
return NULL;
#if WASM_ENABLE_DEBUG_INTERP != 0
if (!is_sub_inst) {
os_mutex_lock(&module->ref_count_lock);
if (module->ref_count != 0) {
LOG_WARNING(
"warning: multiple instances referencing the same module may "
"cause unexpected behaviour during debugging");
}
module->ref_count++;
os_mutex_unlock(&module->ref_count_lock);
}
#endif
/* Check heap size */
heap_size = align_uint(heap_size, 8);
if (heap_size > APP_HEAP_SIZE_MAX)
@ -1133,6 +1147,13 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size,
/* Allocate the memory */
if (!(module_inst = runtime_malloc(sizeof(WASMModuleInstance), error_buf,
error_buf_size))) {
#if WASM_ENABLE_DEBUG_INTERP != 0
if (!is_sub_inst) {
os_mutex_lock(&module->ref_count_lock);
module->ref_count--;
os_mutex_unlock(&module->ref_count_lock);
}
#endif
return NULL;
}
@ -1519,7 +1540,9 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size,
(WASMModuleInstanceCommon *)module_inst);
#endif
(void)global_data_end;
return module_inst;
fail:
wasm_deinstantiate(module_inst, false);
return NULL;
@ -1576,6 +1599,14 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst)
}
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
if (!is_sub_inst) {
os_mutex_lock(&module_inst->module->ref_count_lock);
module_inst->module->ref_count--;
os_mutex_unlock(&module_inst->module->ref_count_lock);
}
#endif
wasm_runtime_free(module_inst);
}
@ -1661,7 +1692,8 @@ wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function,
bool
wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst,
WASMFunctionInstance *func,
unsigned argc, uint32 argv[])
unsigned argc, uint32 argv[],
bool enable_debug)
{
WASMExecEnv *exec_env;
bool ret;
@ -1680,6 +1712,11 @@ wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst,
}
#if WASM_ENABLE_THREAD_MGR != 0
if (enable_debug) {
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_runtime_start_debug_instance(exec_env);
#endif
}
}
#endif

View File

@ -320,7 +320,8 @@ wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function,
bool
wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst,
WASMFunctionInstance *function,
unsigned argc, uint32 argv[]);
unsigned argc, uint32 argv[],
bool enable_debug);
bool
wasm_create_exec_env_singleton(WASMModuleInstance *module_inst);

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

View File

@ -332,7 +332,7 @@ create_cluster_info(WASMCluster *cluster)
if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) {
return NULL;
}
memset(node, 0, sizeof(WASMCluster));
memset(node, 0, sizeof(ClusterInfoNode));
node->thread_list = &node->thread_list_head;
ret = bh_list_init(node->thread_list);

View File

@ -5,6 +5,10 @@
#include "thread_manager.h"
#if WASM_ENABLE_DEBUG_INTERP != 0
#include "debug_engine.h"
#endif
typedef struct {
bh_list_link l;
void (*destroy_cb)(WASMCluster *);
@ -227,6 +231,11 @@ wasm_cluster_destroy(WASMCluster *cluster)
wasm_runtime_free(cluster->stack_tops);
if (cluster->stack_segment_occupied)
wasm_runtime_free(cluster->stack_segment_occupied);
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_debug_instance_destroy(cluster);
#endif
wasm_runtime_free(cluster);
}
@ -488,31 +497,18 @@ wasm_cluster_create_exenv_status()
WASMCurrentEnvStatus *status;
if (!(status = wasm_runtime_malloc(sizeof(WASMCurrentEnvStatus)))) {
goto fail;
return NULL;
}
if (os_mutex_init(&status->wait_lock) != 0)
goto fail1;
if (os_cond_init(&status->wait_cond) != 0)
goto fail2;
status->step_count = 0;
status->signal_flag = 0;
status->running_status = 0;
return status;
fail2:
os_mutex_destroy(&status->wait_lock);
fail1:
wasm_runtime_free(status);
fail:
return NULL;
}
void
wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status)
{
os_mutex_destroy(&status->wait_lock);
os_cond_destroy(&status->wait_cond);
wasm_runtime_free(status);
}
@ -529,29 +525,34 @@ wasm_cluster_clear_thread_signal(WASMExecEnv *exec_env)
exec_env->current_status->signal_flag = 0;
}
void
wasm_cluster_wait_thread_status(WASMExecEnv *exec_env, uint32 *status)
{
os_mutex_lock(&exec_env->current_status->wait_lock);
while (wasm_cluster_thread_is_running(exec_env)) {
os_cond_wait(&exec_env->current_status->wait_cond,
&exec_env->current_status->wait_lock);
}
*status = exec_env->current_status->signal_flag;
os_mutex_unlock(&exec_env->current_status->wait_lock);
}
void
wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo)
{
exec_env->current_status->signal_flag = signo;
}
static void
notify_debug_instance(WASMExecEnv *exec_env)
{
WASMCluster *cluster;
cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster);
if (!cluster->debug_inst) {
return;
}
os_mutex_lock(&cluster->debug_inst->wait_lock);
os_cond_signal(&cluster->debug_inst->wait_cond);
os_mutex_unlock(&cluster->debug_inst->wait_lock);
}
void
wasm_cluster_thread_stopped(WASMExecEnv *exec_env)
{
exec_env->current_status->running_status = STATUS_STOP;
os_cond_signal(&exec_env->current_status->wait_cond);
notify_debug_instance(exec_env);
}
void
@ -578,7 +579,7 @@ void
wasm_cluster_thread_exited(WASMExecEnv *exec_env)
{
exec_env->current_status->running_status = STATUS_EXIT;
os_cond_signal(&exec_env->current_status->wait_cond);
notify_debug_instance(exec_env);
}
void
@ -595,7 +596,14 @@ wasm_cluster_thread_step(WASMExecEnv *exec_env)
exec_env->current_status->running_status = STATUS_STEP;
os_cond_signal(&exec_env->wait_cond);
}
#endif
void
wasm_cluster_set_debug_inst(WASMCluster *cluster, WASMDebugInstance *inst)
{
cluster->debug_inst = inst;
}
#endif /* end of WASM_ENABLE_DEBUG_INTERP */
int32
wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val)

View File

@ -39,6 +39,8 @@ typedef struct WASMCurrentEnvStatus {
korp_cond wait_cond;
} WASMCurrentEnvStatus;
typedef struct WASMDebugInstance WASMDebugInstance;
WASMCurrentEnvStatus *
wasm_cluster_create_exenv_status();
@ -69,6 +71,9 @@ wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo);
void
wasm_cluster_thread_step(WASMExecEnv *exec_env);
void
wasm_cluster_set_debug_inst(WASMCluster *cluster, WASMDebugInstance *inst);
#endif
typedef struct WASMCluster {
struct WASMCluster *next;
@ -84,6 +89,9 @@ typedef struct WASMCluster {
uint32 stack_size;
/* Record which segments are occupied */
bool *stack_segment_occupied;
#if WASM_ENABLE_DEBUG_INTERP != 0
WASMDebugInstance *debug_inst;
#endif
} WASMCluster;
void

View File

@ -146,6 +146,10 @@ Currently we only profile the memory consumption of module, module_instance and
> Note: The WAMR application entry (`core/iwasm/common/wasm_application.c`) encapsulate some common process to instantiate, execute the wasm functions and print the results. Some platform related APIs are used in these functions, so you can enable this flag to exclude this file if your platform doesn't support those APIs.
> *Don't enable this flag if you are building `product-mini`*
#### **Enable source debugging features**
- **WAMR_BUILD_DEBUG_INTERP**=1/0, default to 0 if not set
> Note: There are some other setup required by source debugging, please refer to [source_debugging.md](./source_debugging.md) for more details.
**Combination of configurations:**
We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command:

View File

@ -89,3 +89,51 @@ lldb-12 iwasm -- test.aot
```
Then you can use lldb commands to debug both wamr runtime and your wasm application in ***current terminal***
## Enable debugging in embedders (for interpreter)
There are three steps to enable debugging in embedders
1. Set the debug parameters when initializing the runtime environment:
``` c
RuntimeInitArgs init_args;
memset(&init_args, 0, sizeof(RuntimeInitArgs));
/* ... */
strcpy(init_args.ip_addr, "127.0.0.1");
init_args.instance_port = 1234;
/*
* Or set port to 0 to use a port assigned by os
* init_args.instance_port = 0;
*/
if (!wasm_runtime_full_init(&init_args)) {
return false;
}
```
2. Use `wasm_runtime_start_debug_instance` to create the debug instance:
``` c
/*
initialization, loading and instantiating
...
*/
exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
uint32_t debug_port = wasm_runtime_start_debug_instance();
```
3. Enable source debugging features during building
You can use `-DWAMR_BUILD_DEBUG_INTERP=1` during cmake configuration
Or you can set it directly in `cmake` files:
``` cmake
set (WAMR_BUILD_DEBUG_INTERP 1)
```
### Attentions
- Debugging `multi-thread wasm module` is not supported, if your wasm module use pthread APIs (see [pthread_library.md](./pthread_library.md)), or the embedder use `wasm_runtime_spawn_thread` to create new wasm threads, then there may be **unexpected behaviour** during debugging.
> Note: This attention is about "wasm thread" rather than native threads. Executing wasm functions in several different native threads will **not** affect the normal behaviour of debugging feature.
- When using source debugging features, **don't** create multiple `wasm_instance` from the same `wasm_module`, because the debugger may change the bytecode (set/unset breakpoints) of the `wasm_module`. If you do need several instance from the same bytecode, you need to copy the bytecode to a new butter, then load a new `wasm_module`, and then instantiate the new wasm module to get the new instance.