wasm-micro-runtime/core/iwasm/libraries/debug-engine/debug_engine.c
Wenyong Huang a182926a73
Refactor interpreter/AOT module instance layout (#1559)
Refactor the layout of interpreter and AOT module instance:
- Unify the interp/AOT module instance, use the same WASMModuleInstance/
  WASMMemoryInstance/WASMTableInstance data structures for both interpreter
  and AOT
- Make the offset of most fields the same in module instance for both interpreter
  and AOT, append memory instance structure, global data and table instances to
  the end of module instance for interpreter mode (like AOT mode)
- For extra fields in WASM module instance, use WASMModuleInstanceExtra to
  create a field `e` for interpreter
- Change the LLVM JIT module instance creating process, LLVM JIT uses the WASM
  module and module instance same as interpreter/Fast-JIT mode. So that Fast JIT
  and LLVM JIT can access the same data structures, and make it possible to
  implement the Multi-tier JIT (tier-up from Fast JIT to LLVM JIT) in the future
- Unify some APIs: merge some APIs for module instance and memory instance's
  related operations (only implement one copy)

Note that the AOT ABI is same, the AOT file format, AOT relocation types, how AOT
code accesses the AOT module instance and so on are kept unchanged.

Refer to:
https://github.com/bytecodealliance/wasm-micro-runtime/issues/1384
2022-10-18 10:59:28 +08:00

1230 lines
35 KiB
C

/*
* Copyright (C) 2021 Ant Group. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "debug_engine.h"
#include "gdbserver.h"
#include "handler.h"
#include "bh_platform.h"
#include "wasm_interp.h"
#include "wasm_opcode.h"
#include "wasm_runtime.h"
static const uint8 break_instr[] = { DEBUG_OP_BREAK };
typedef struct WASMDebugEngine {
struct WASMDebugEngine *next;
WASMDebugControlThread *control_thread;
char ip_addr[128];
int32 process_base_port;
bh_list debug_instance_list;
korp_mutex instance_list_lock;
} WASMDebugEngine;
void
on_thread_stop_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env)
{
os_mutex_lock(&debug_inst->wait_lock);
debug_inst->stopped_thread = exec_env;
if (debug_inst->current_state == DBG_LAUNCHING) {
/* In launching phase, send a signal so that handle_threadstop_request
* can be woken up */
os_cond_signal(&debug_inst->wait_cond);
}
os_mutex_unlock(&debug_inst->wait_lock);
}
static WASMDebugEngine *g_debug_engine;
static uint32 current_instance_id = 1;
static uint32
allocate_instance_id()
{
uint32 id;
bh_assert(g_debug_engine);
os_mutex_lock(&g_debug_engine->instance_list_lock);
id = current_instance_id++;
os_mutex_unlock(&g_debug_engine->instance_list_lock);
return id;
}
static bool
should_stop(WASMDebugControlThread *control_thread)
{
return control_thread->status != RUNNING;
}
static void *
control_thread_routine(void *arg)
{
WASMDebugInstance *debug_inst = (WASMDebugInstance *)arg;
WASMDebugControlThread *control_thread = NULL;
control_thread = debug_inst->control_thread;
bh_assert(control_thread);
os_mutex_lock(&debug_inst->wait_lock);
control_thread->status = RUNNING;
debug_inst->id = allocate_instance_id();
control_thread->debug_engine = g_debug_engine;
control_thread->debug_instance = debug_inst;
bh_strcpy_s(control_thread->ip_addr, sizeof(control_thread->ip_addr),
g_debug_engine->ip_addr);
if (control_thread->port == -1) {
control_thread->port =
(g_debug_engine->process_base_port == 0)
? 0
: g_debug_engine->process_base_port + debug_inst->id - 1;
}
LOG_WARNING("control thread of debug object %p start\n", debug_inst);
control_thread->server =
wasm_create_gdbserver(control_thread->ip_addr, &control_thread->port);
if (!control_thread->server) {
LOG_ERROR("Failed to create debug server\n");
control_thread->port = 0;
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&debug_inst->wait_lock);
return NULL;
}
control_thread->server->thread = control_thread;
/*
* 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);
if (!should_stop(control_thread)) {
/* send thread stop reply */
if (debug_inst->stopped_thread
&& debug_inst->current_state == APP_RUNNING) {
uint32 status;
korp_tid tid;
status =
(uint32)
debug_inst->stopped_thread->current_status->signal_flag;
tid = debug_inst->stopped_thread->handle;
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 */
os_cond_signal(&debug_inst->wait_cond);
}
}
/* Processing incoming requests */
if (!wasm_gdbserver_handle_packet(control_thread->server)) {
control_thread->status = STOPPED;
}
}
else {
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 *
wasm_debug_control_thread_create(WASMDebugInstance *debug_instance, int32 port)
{
WASMDebugControlThread *control_thread;
if (!(control_thread =
wasm_runtime_malloc(sizeof(WASMDebugControlThread)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return NULL;
}
memset(control_thread, 0, sizeof(WASMDebugControlThread));
control_thread->port = port;
if (os_mutex_init(&control_thread->wait_lock) != 0)
goto fail;
debug_instance->control_thread = control_thread;
os_mutex_lock(&debug_instance->wait_lock);
if (0
!= os_thread_create(&control_thread->tid, control_thread_routine,
debug_instance, APP_THREAD_STACK_SIZE_DEFAULT)) {
os_mutex_unlock(&debug_instance->wait_lock);
goto fail1;
}
/* wait until the debug control thread ready */
os_cond_wait(&debug_instance->wait_cond, &debug_instance->wait_lock);
os_mutex_unlock(&debug_instance->wait_lock);
if (!control_thread->server) {
os_thread_join(control_thread->tid, NULL);
goto fail1;
}
os_mutex_lock(&g_debug_engine->instance_list_lock);
/* create control thread success, append debug instance to debug engine */
bh_list_insert(&g_debug_engine->debug_instance_list, debug_instance);
os_mutex_unlock(&g_debug_engine->instance_list_lock);
wasm_cluster_send_signal_all(debug_instance->cluster, WAMR_SIG_STOP);
return control_thread;
fail1:
os_mutex_destroy(&control_thread->wait_lock);
fail:
wasm_runtime_free(control_thread);
return NULL;
}
static void
wasm_debug_control_thread_destroy(WASMDebugInstance *debug_instance)
{
WASMDebugControlThread *control_thread = debug_instance->control_thread;
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);
os_mutex_unlock(&control_thread->wait_lock);
os_thread_join(control_thread->tid, NULL);
wasm_runtime_free(control_thread->server);
os_mutex_destroy(&control_thread->wait_lock);
wasm_runtime_free(control_thread);
}
static WASMDebugEngine *
wasm_debug_engine_create()
{
WASMDebugEngine *engine;
if (!(engine = wasm_runtime_malloc(sizeof(WASMDebugEngine)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return NULL;
}
memset(engine, 0, sizeof(WASMDebugEngine));
if (os_mutex_init(&engine->instance_list_lock) != 0) {
wasm_runtime_free(engine);
LOG_ERROR("WASM Debug Engine error: failed to init mutex");
return NULL;
}
/* reset current instance id */
current_instance_id = 1;
bh_list_init(&engine->debug_instance_list);
return engine;
}
void
wasm_debug_engine_destroy()
{
if (g_debug_engine) {
wasm_debug_handler_deinit();
os_mutex_destroy(&g_debug_engine->instance_list_lock);
wasm_runtime_free(g_debug_engine);
g_debug_engine = NULL;
}
}
bool
wasm_debug_engine_init(char *ip_addr, int32 process_port)
{
if (wasm_debug_handler_init() != 0) {
return false;
}
if (g_debug_engine == NULL) {
g_debug_engine = wasm_debug_engine_create();
}
if (g_debug_engine) {
g_debug_engine->process_base_port =
(process_port > 0) ? process_port : 0;
if (ip_addr)
snprintf(g_debug_engine->ip_addr, sizeof(g_debug_engine->ip_addr),
"%s", ip_addr);
else
snprintf(g_debug_engine->ip_addr, sizeof(g_debug_engine->ip_addr),
"%s", "127.0.0.1");
}
else {
wasm_debug_handler_deinit();
}
return g_debug_engine != NULL ? true : false;
}
/* A debug Instance is a debug "process" in gdb remote protocol
and bound to a runtime cluster */
WASMDebugInstance *
wasm_debug_instance_create(WASMCluster *cluster, int32 port)
{
WASMDebugInstance *instance;
WASMExecEnv *exec_env = NULL;
wasm_module_inst_t module_inst = NULL;
if (!g_debug_engine) {
return NULL;
}
if (!(instance = wasm_runtime_malloc(sizeof(WASMDebugInstance)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
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;
exec_env = bh_list_first_elem(&cluster->exec_env_list);
bh_assert(exec_env);
instance->current_tid = exec_env->handle;
module_inst = wasm_runtime_get_module_inst(exec_env);
bh_assert(module_inst);
/* Allocate linear memory for evaluating expressions during debugging. If
* the allocation failed, the debugger will not be able to evaluate
* expressions */
instance->exec_mem_info.size = DEBUG_EXECUTION_MEMORY_SIZE;
instance->exec_mem_info.start_offset = wasm_runtime_module_malloc(
module_inst, instance->exec_mem_info.size, NULL);
if (instance->exec_mem_info.start_offset == 0) {
LOG_WARNING(
"WASM Debug Engine warning: failed to allocate linear memory for "
"execution. \n"
"Will not be able to evaluate expressions during "
"debugging");
}
instance->exec_mem_info.current_pos = instance->exec_mem_info.start_offset;
if (!wasm_debug_control_thread_create(instance, port)) {
LOG_ERROR("WASM Debug Engine error: failed to create control thread");
goto fail3;
}
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
wasm_debug_instance_destroy_breakpoints(WASMDebugInstance *instance)
{
WASMDebugBreakPoint *breakpoint, *next_bp;
breakpoint = bh_list_first_elem(&instance->break_point_list);
while (breakpoint) {
next_bp = bh_list_elem_next(breakpoint);
bh_list_remove(&instance->break_point_list, breakpoint);
wasm_runtime_free(breakpoint);
breakpoint = next_bp;
}
}
void
wasm_debug_instance_destroy(WASMCluster *cluster)
{
WASMDebugInstance *instance = NULL;
if (!g_debug_engine) {
return;
}
instance = cluster->debug_inst;
if (instance) {
/* destroy control thread */
wasm_debug_control_thread_destroy(instance);
os_mutex_lock(&g_debug_engine->instance_list_lock);
bh_list_remove(&g_debug_engine->debug_instance_list, instance);
os_mutex_unlock(&g_debug_engine->instance_list_lock);
/* 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;
}
}
WASMExecEnv *
wasm_debug_instance_get_current_env(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env = NULL;
if (instance) {
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
if (exec_env->handle == instance->current_tid)
break;
exec_env = bh_list_elem_next(exec_env);
}
}
return exec_env;
}
#if WASM_ENABLE_LIBC_WASI != 0
bool
wasm_debug_instance_get_current_object_name(WASMDebugInstance *instance,
char name_buffer[], uint32 len)
{
WASMExecEnv *exec_env;
WASIArguments *wasi_args;
WASMModuleInstance *module_inst;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
wasi_args = &module_inst->module->wasi_args;
if (wasi_args && wasi_args->argc > 0) {
char *argv_name = wasi_args->argv[0];
uint32 name_len = (uint32)strlen(argv_name);
printf("the module name is %s\n", argv_name);
if (len - 1 >= name_len)
bh_strcpy_s(name_buffer, len, argv_name);
else
bh_strcpy_s(name_buffer, len, argv_name + (name_len + 1 - len));
return true;
}
return false;
}
#endif
uint64
wasm_debug_instance_get_pid(WASMDebugInstance *instance)
{
if (instance != NULL) {
return (uint64)instance->id;
}
return (uint64)0;
}
korp_tid
wasm_debug_instance_get_tid(WASMDebugInstance *instance)
{
if (instance != NULL) {
return instance->current_tid;
}
return (korp_tid)(uintptr_t)0;
}
uint32
wasm_debug_instance_get_tids(WASMDebugInstance *instance, korp_tid tids[],
uint32 len)
{
WASMExecEnv *exec_env;
uint32 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) {
/* 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", threads_num);
return threads_num;
}
uint32
wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, korp_tid tid)
{
WASMExecEnv *exec_env = NULL;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
if (exec_env->handle == tid) {
return (uint32)exec_env->current_status->signal_flag;
}
exec_env = bh_list_elem_next(exec_env);
}
return 0;
}
void
wasm_debug_instance_set_cur_thread(WASMDebugInstance *instance, korp_tid tid)
{
instance->current_tid = tid;
}
uint64
wasm_debug_instance_get_pc(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return 0;
exec_env = wasm_debug_instance_get_current_env(instance);
if ((exec_env != NULL) && (exec_env->cur_frame != NULL)
&& (exec_env->cur_frame->ip != NULL)) {
WASMModuleInstance *module_inst =
(WASMModuleInstance *)exec_env->module_inst;
return WASM_ADDR(
WasmObj, instance->id,
(exec_env->cur_frame->ip - module_inst->module->load_addr));
}
return 0;
}
uint64
wasm_debug_instance_get_load_addr(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return WASM_ADDR(WasmInvalid, 0, 0);
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (exec_env) {
return WASM_ADDR(WasmObj, instance->id, 0);
}
return WASM_ADDR(WasmInvalid, 0, 0);
}
WASMDebugMemoryInfo *
wasm_debug_instance_get_memregion(WASMDebugInstance *instance, uint64 addr)
{
WASMDebugMemoryInfo *mem_info;
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMMemoryInstance *memory;
uint32 num_bytes_per_page;
uint32 linear_mem_size = 0;
if (!instance)
return NULL;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return NULL;
if (!(mem_info = wasm_runtime_malloc(sizeof(WASMDebugMemoryInfo)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return NULL;
}
memset(mem_info, 0, sizeof(WASMDebugMemoryInfo));
mem_info->start = WASM_ADDR(WasmInvalid, 0, 0);
mem_info->size = 0;
mem_info->name[0] = '\0';
mem_info->permisson[0] = '\0';
module_inst = (WASMModuleInstance *)exec_env->module_inst;
switch (WASM_ADDR_TYPE(addr)) {
case WasmObj:
if (WASM_ADDR_OFFSET(addr) < module_inst->module->load_size) {
mem_info->start = WASM_ADDR(WasmObj, instance->id, 0);
mem_info->size = module_inst->module->load_size;
snprintf(mem_info->name, sizeof(mem_info->name), "%s",
"module");
snprintf(mem_info->permisson, sizeof(mem_info->permisson), "%s",
"rx");
}
break;
case WasmMemory:
{
memory = wasm_get_default_memory(module_inst);
if (memory) {
num_bytes_per_page = memory->num_bytes_per_page;
linear_mem_size = num_bytes_per_page * memory->cur_page_count;
}
if (WASM_ADDR_OFFSET(addr) < linear_mem_size) {
mem_info->start = WASM_ADDR(WasmMemory, instance->id, 0);
mem_info->size = linear_mem_size;
snprintf(mem_info->name, sizeof(mem_info->name), "%s",
"memory");
snprintf(mem_info->permisson, sizeof(mem_info->permisson), "%s",
"rw");
}
break;
}
default:
mem_info->start = WASM_ADDR(WasmInvalid, 0, 0);
mem_info->size = 0;
}
return mem_info;
}
void
wasm_debug_instance_destroy_memregion(WASMDebugInstance *instance,
WASMDebugMemoryInfo *mem_info)
{
wasm_runtime_free(mem_info);
}
bool
wasm_debug_instance_get_obj_mem(WASMDebugInstance *instance, uint64 offset,
char *buf, uint64 *size)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMDebugBreakPoint *breakpoint;
WASMFastOPCodeNode *fast_opcode;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
if (offset + *size > module_inst->module->load_size) {
LOG_VERBOSE("wasm_debug_instance_get_data_mem size over flow!\n");
*size = module_inst->module->load_size >= offset
? module_inst->module->load_size - offset
: 0;
}
bh_memcpy_s(buf, (uint32)*size, module_inst->module->load_addr + offset,
(uint32)*size);
breakpoint = bh_list_first_elem(&instance->break_point_list);
while (breakpoint) {
if (offset <= breakpoint->addr && breakpoint->addr < offset + *size) {
bh_memcpy_s(buf + (breakpoint->addr - offset), sizeof(break_instr),
&breakpoint->orignal_data, sizeof(break_instr));
}
breakpoint = bh_list_elem_next(breakpoint);
}
fast_opcode = bh_list_first_elem(&module_inst->module->fast_opcode_list);
while (fast_opcode) {
if (offset <= fast_opcode->offset
&& fast_opcode->offset < offset + *size) {
*(uint8 *)(buf + (fast_opcode->offset - offset)) =
fast_opcode->orig_op;
}
fast_opcode = bh_list_elem_next(fast_opcode);
}
return true;
}
bool
wasm_debug_instance_get_linear_mem(WASMDebugInstance *instance, uint64 offset,
char *buf, uint64 *size)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMMemoryInstance *memory;
uint32 num_bytes_per_page;
uint32 linear_mem_size;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
memory = wasm_get_default_memory(module_inst);
if (memory) {
num_bytes_per_page = memory->num_bytes_per_page;
linear_mem_size = num_bytes_per_page * memory->cur_page_count;
if (offset + *size > linear_mem_size) {
LOG_VERBOSE("wasm_debug_instance_get_linear_mem size over flow!\n");
*size = linear_mem_size >= offset ? linear_mem_size - offset : 0;
}
bh_memcpy_s(buf, (uint32)*size, memory->memory_data + offset,
(uint32)*size);
return true;
}
return false;
}
bool
wasm_debug_instance_set_linear_mem(WASMDebugInstance *instance, uint64 offset,
char *buf, uint64 *size)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
WASMMemoryInstance *memory;
uint32 num_bytes_per_page;
uint32 linear_mem_size;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
memory = wasm_get_default_memory(module_inst);
if (memory) {
num_bytes_per_page = memory->num_bytes_per_page;
linear_mem_size = num_bytes_per_page * memory->cur_page_count;
if (offset + *size > linear_mem_size) {
LOG_VERBOSE("wasm_debug_instance_get_linear_mem size over flow!\n");
*size = linear_mem_size >= offset ? linear_mem_size - offset : 0;
}
bh_memcpy_s(memory->memory_data + offset, (uint32)*size, buf,
(uint32)*size);
return true;
}
return false;
}
bool
wasm_debug_instance_get_mem(WASMDebugInstance *instance, uint64 addr, char *buf,
uint64 *size)
{
switch (WASM_ADDR_TYPE(addr)) {
case WasmMemory:
return wasm_debug_instance_get_linear_mem(
instance, WASM_ADDR_OFFSET(addr), buf, size);
break;
case WasmObj:
return wasm_debug_instance_get_obj_mem(
instance, WASM_ADDR_OFFSET(addr), buf, size);
break;
default:
return false;
}
}
bool
wasm_debug_instance_set_mem(WASMDebugInstance *instance, uint64 addr, char *buf,
uint64 *size)
{
switch (WASM_ADDR_TYPE(addr)) {
case WasmMemory:
return wasm_debug_instance_set_linear_mem(
instance, WASM_ADDR_OFFSET(addr), buf, size);
break;
case WasmObj:
default:
return false;
}
}
WASMDebugInstance *
wasm_exec_env_get_instance(WASMExecEnv *exec_env)
{
WASMDebugInstance *instance = NULL;
if (!g_debug_engine) {
return NULL;
}
os_mutex_lock(&g_debug_engine->instance_list_lock);
instance = bh_list_first_elem(&g_debug_engine->debug_instance_list);
while (instance) {
if (instance->cluster == exec_env->cluster)
break;
instance = bh_list_elem_next(instance);
}
os_mutex_unlock(&g_debug_engine->instance_list_lock);
return instance;
}
uint32
wasm_debug_instance_get_call_stack_pcs(WASMDebugInstance *instance,
korp_tid tid, uint64 buf[], uint64 size)
{
WASMExecEnv *exec_env;
struct WASMInterpFrame *frame;
uint32 i = 0;
if (!instance)
return 0;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
if (exec_env->handle == tid) {
WASMModuleInstance *module_inst =
(WASMModuleInstance *)exec_env->module_inst;
frame = exec_env->cur_frame;
while (frame && i < size) {
if (frame->ip != NULL) {
buf[i++] =
WASM_ADDR(WasmObj, instance->id,
(frame->ip - module_inst->module->load_addr));
}
frame = frame->prev_frame;
}
return i;
}
exec_env = bh_list_elem_next(exec_env);
}
return 0;
}
bool
wasm_debug_instance_add_breakpoint(WASMDebugInstance *instance, uint64 addr,
uint64 length)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
uint64 offset;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
if (WASM_ADDR_TYPE(addr) != WasmObj)
return false;
offset = WASM_ADDR_OFFSET(addr);
if (length >= sizeof(break_instr)) {
if (offset + sizeof(break_instr) <= module_inst->module->load_size) {
WASMDebugBreakPoint *breakpoint;
if (!(breakpoint =
wasm_runtime_malloc(sizeof(WASMDebugBreakPoint)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
return false;
}
memset(breakpoint, 0, sizeof(WASMDebugBreakPoint));
breakpoint->addr = offset;
/* TODO: how to if more than one breakpoints are set
at the same addr? */
bh_memcpy_s(&breakpoint->orignal_data, (uint32)sizeof(break_instr),
module_inst->module->load_addr + offset,
(uint32)sizeof(break_instr));
bh_memcpy_s(module_inst->module->load_addr + offset,
(uint32)sizeof(break_instr), break_instr,
(uint32)sizeof(break_instr));
bh_list_insert(&instance->break_point_list, breakpoint);
return true;
}
}
return false;
}
bool
wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr,
uint64 length)
{
WASMExecEnv *exec_env;
WASMModuleInstance *module_inst;
uint64 offset;
if (!instance)
return false;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
if (WASM_ADDR_TYPE(addr) != WasmObj)
return false;
offset = WASM_ADDR_OFFSET(addr);
if (length >= sizeof(break_instr)) {
if (offset + sizeof(break_instr) <= module_inst->module->load_size) {
WASMDebugBreakPoint *breakpoint =
bh_list_first_elem(&instance->break_point_list);
while (breakpoint) {
WASMDebugBreakPoint *next_break = bh_list_elem_next(breakpoint);
if (breakpoint->addr == offset) {
/* TODO: how to if more than one breakpoints are set
at the same addr? */
bh_memcpy_s(module_inst->module->load_addr + offset,
(uint32)sizeof(break_instr),
&breakpoint->orignal_data,
(uint32)sizeof(break_instr));
bh_list_remove(&instance->break_point_list, breakpoint);
wasm_runtime_free(breakpoint);
}
breakpoint = next_break;
}
}
}
return true;
}
bool
wasm_debug_instance_continue(WASMDebugInstance *instance)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
if (instance->current_state == APP_RUNNING) {
LOG_VERBOSE("Already in running state, ignore continue request");
return false;
}
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
while (exec_env) {
wasm_cluster_thread_continue(exec_env);
exec_env = bh_list_elem_next(exec_env);
}
instance->current_state = APP_RUNNING;
return true;
}
bool
wasm_debug_instance_interrupt_all_threads(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;
while (exec_env) {
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP);
exec_env = bh_list_elem_next(exec_env);
}
return true;
}
bool
wasm_debug_instance_kill(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;
while (exec_env) {
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM);
if (instance->current_state == APP_STOPPED) {
/* Resume all threads so they can receive the TERM signal */
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);
}
instance->current_state = APP_RUNNING;
return true;
}
bool
wasm_debug_instance_singlestep(WASMDebugInstance *instance, korp_tid tid)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
if (instance->current_state == APP_RUNNING) {
LOG_VERBOSE("Already in running state, ignore step request");
return false;
}
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
if (!exec_env)
return false;
while (exec_env) {
if (exec_env->handle == tid || tid == (korp_tid)(uintptr_t)~0LL) {
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_SINGSTEP);
wasm_cluster_thread_step(exec_env);
}
exec_env = bh_list_elem_next(exec_env);
}
instance->current_state = APP_RUNNING;
return true;
}
bool
wasm_debug_instance_get_local(WASMDebugInstance *instance, int32 frame_index,
int32 local_index, char buf[], int32 *size)
{
WASMExecEnv *exec_env;
struct WASMInterpFrame *frame;
WASMFunctionInstance *cur_func;
uint8 local_type = 0xFF;
uint32 local_offset;
int32 param_count;
int32 fi = 0;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
frame = exec_env->cur_frame;
while (frame && fi++ != frame_index) {
frame = frame->prev_frame;
}
if (!frame)
return false;
cur_func = frame->function;
if (!cur_func)
return false;
param_count = cur_func->param_count;
if (local_index >= param_count + cur_func->local_count)
return false;
local_offset = cur_func->local_offsets[local_index];
if (local_index < param_count)
local_type = cur_func->param_types[local_index];
else if (local_index < cur_func->local_count + param_count)
local_type = cur_func->local_types[local_index - param_count];
switch (local_type) {
case VALUE_TYPE_I32:
case VALUE_TYPE_F32:
*size = 4;
bh_memcpy_s(buf, 4, (char *)(frame->lp + local_offset), 4);
break;
case VALUE_TYPE_I64:
case VALUE_TYPE_F64:
*size = 8;
bh_memcpy_s(buf, 8, (char *)(frame->lp + local_offset), 8);
break;
default:
*size = 0;
break;
}
return true;
}
bool
wasm_debug_instance_get_global(WASMDebugInstance *instance, int32 frame_index,
int32 global_index, char buf[], int32 *size)
{
WASMExecEnv *exec_env;
struct WASMInterpFrame *frame;
WASMModuleInstance *module_inst;
WASMGlobalInstance *globals, *global;
uint8 *global_addr;
uint8 global_type = 0xFF;
uint8 *global_data;
int32 fi = 0;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
frame = exec_env->cur_frame;
while (frame && fi++ != frame_index) {
frame = frame->prev_frame;
}
if (!frame)
return false;
module_inst = (WASMModuleInstance *)exec_env->module_inst;
global_data = module_inst->global_data;
globals = module_inst->e->globals;
if ((global_index < 0)
|| ((uint32)global_index >= module_inst->e->global_count)) {
return false;
}
global = globals + global_index;
#if WASM_ENABLE_MULTI_MODULE == 0
global_addr = global_data + global->data_offset;
#else
global_addr = global->import_global_inst
? global->import_module_inst->global_data
+ global->import_global_inst->data_offset
: global_data + global->data_offset;
#endif
global_type = global->type;
switch (global_type) {
case VALUE_TYPE_I32:
case VALUE_TYPE_F32:
*size = 4;
bh_memcpy_s(buf, 4, (char *)(global_addr), 4);
break;
case VALUE_TYPE_I64:
case VALUE_TYPE_F64:
*size = 8;
bh_memcpy_s(buf, 8, (char *)(global_addr), 8);
break;
default:
*size = 0;
break;
}
return true;
}
uint64
wasm_debug_instance_mmap(WASMDebugInstance *instance, uint32 size,
int32 map_prot)
{
WASMExecEnv *exec_env;
uint32 offset = 0;
(void)map_prot;
if (!instance)
return 0;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return 0;
if (instance->exec_mem_info.start_offset == 0) {
return 0;
}
if ((uint64)instance->exec_mem_info.current_pos
- instance->exec_mem_info.start_offset + size
<= (uint64)instance->exec_mem_info.size) {
offset = instance->exec_mem_info.current_pos;
instance->exec_mem_info.current_pos += size;
}
if (offset == 0) {
LOG_WARNING("the memory may be not enough for debug, try use larger "
"--heap-size");
return 0;
}
return WASM_ADDR(WasmMemory, 0, offset);
}
bool
wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr)
{
WASMExecEnv *exec_env;
if (!instance)
return false;
exec_env = wasm_debug_instance_get_current_env(instance);
if (!exec_env)
return false;
if (instance->exec_mem_info.start_offset == 0) {
return false;
}
(void)addr;
/* Currently we don't support to free the execution memory, simply return
* true here */
return true;
}