Implement source debugging for interpreter and AOT (#769)

Implement source debugging feature for classic interpreter and AOT:
- use `cmake -DWAMR_BUILD_DEBUG_INTERP=1` to enable interpreter debugging
- use `cmake -DWAMR_BUILD_DEBUG_AOT=1` to enable AOT debugging

See doc/source_debugging.md for more details.
This commit is contained in:
Wenyong Huang
2021-09-29 13:36:46 +08:00
committed by GitHub
parent b5a67cb91e
commit 9ef37dd781
55 changed files with 10092 additions and 63 deletions

View File

@ -316,6 +316,13 @@ typedef struct StringNode {
char *str;
} StringNode, *StringList;
#if WASM_ENABLE_DEBUG_INTERP != 0
typedef struct WASMFastOPCodeNode {
struct WASMFastOPCodeNode *next;
uint64 offset;
uint8 orig_op;
} WASMFastOPCodeNode;
#endif
struct WASMModule {
/* Module type, for module loaded from WASM bytecode binary,
this field is Wasm_Module_Bytecode;
@ -404,6 +411,13 @@ struct WASMModule {
bh_list import_module_list_head;
bh_list *import_module_list;
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_DEBUG_AOT != 0
bh_list fast_opcode_list;
uint8 *buf_code;
uint8 *load_addr;
uint64 load_size;
uint64 buf_code_size;
#endif
};
typedef struct BlockType {

View File

@ -12,6 +12,9 @@
#if WASM_ENABLE_SHARED_MEMORY != 0
#include "../common/wasm_shared_memory.h"
#endif
#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0
#include "../libraries/thread-mgr/thread_manager.h"
#endif
typedef int32 CellType_I32;
typedef int64 CellType_I64;
@ -848,27 +851,70 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst,
#endif
#if WASM_ENABLE_THREAD_MGR != 0
#if WASM_ENABLE_DEBUG_INTERP != 0
#define CHECK_SUSPEND_FLAGS() do { \
if (IS_WAMR_TERM_SIG(exec_env->current_status->signal_flag)) { \
return; \
} \
if (IS_WAMR_STOP_SIG(exec_env->current_status->signal_flag)) { \
SYNC_ALL_TO_FRAME(); \
wasm_cluster_thread_stopped(exec_env); \
wasm_cluster_thread_waiting_run(exec_env); \
} \
} while (0)
#else
#define CHECK_SUSPEND_FLAGS() do { \
if (exec_env->suspend_flags.flags != 0) { \
if (exec_env->suspend_flags.flags & 0x01) { \
/* terminate current thread */ \
return; \
} \
/* TODO: support suspend and breakpoint */ \
while (exec_env->suspend_flags.flags & 0x02){ \
/* suspend current thread */ \
os_cond_wait(&exec_env->wait_cond, \
&exec_env->wait_lock); \
} \
} \
} while (0)
#endif
#endif
#if WASM_ENABLE_LABELS_AS_VALUES != 0
#define HANDLE_OP(opcode) HANDLE_##opcode
#define FETCH_OPCODE_AND_DISPATCH() goto *handle_table[*frame_ip++]
#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0
#define HANDLE_OP_END() \
do { \
while (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP \
&& exec_env->current_status->step_count++ == 1) { \
exec_env->current_status->step_count = 0; \
SYNC_ALL_TO_FRAME(); \
wasm_cluster_thread_stopped(exec_env); \
wasm_cluster_thread_waiting_run(exec_env); \
} \
goto *handle_table[*frame_ip++]; \
} while (0)
#else
#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH()
#endif
#else /* else of WASM_ENABLE_LABELS_AS_VALUES */
#define HANDLE_OP(opcode) case opcode
#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0
#define HANDLE_OP_END() \
if (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP \
&& exec_env->current_status->step_count++ == 2) { \
exec_env->current_status->step_count = 0; \
SYNC_ALL_TO_FRAME(); \
wasm_cluster_thread_stopped(exec_env); \
wasm_cluster_thread_waiting_run(exec_env); \
} \
continue
#else
#define HANDLE_OP_END() continue
#endif
#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */
@ -941,6 +987,16 @@ handle_op_block:
else if (cache_items[1].start_addr == frame_ip) {
end_addr = cache_items[1].end_addr;
}
#if WASM_ENABLE_DEBUG_INTERP != 0
else if (!wasm_loader_find_block_addr(exec_env,
(BlockAddr*)exec_env->block_addr_cache,
frame_ip, (uint8*)-1,
LABEL_TYPE_BLOCK,
&else_addr, &end_addr)) {
wasm_set_exception(module, "find block address failed");
goto got_exception;
}
#endif
else {
end_addr = NULL;
}
@ -978,7 +1034,8 @@ handle_op_if:
else_addr = cache_items[1].else_addr;
end_addr = cache_items[1].end_addr;
}
else if (!wasm_loader_find_block_addr((BlockAddr*)exec_env->block_addr_cache,
else if (!wasm_loader_find_block_addr(exec_env,
(BlockAddr*)exec_env->block_addr_cache,
frame_ip, (uint8*)-1,
LABEL_TYPE_IF,
&else_addr, &end_addr)) {
@ -1030,7 +1087,8 @@ handle_op_if:
label_pop_csp_n:
POP_CSP_N(depth);
if (!frame_ip) { /* must be label pushed by WASM_OP_BLOCK */
if (!wasm_loader_find_block_addr((BlockAddr*)exec_env->block_addr_cache,
if (!wasm_loader_find_block_addr(exec_env,
(BlockAddr*)exec_env->block_addr_cache,
(frame_csp - 1)->begin_addr, (uint8*)-1,
LABEL_TYPE_BLOCK,
&else_addr, &end_addr)) {
@ -3178,7 +3236,17 @@ label_pop_csp_n:
frame_sp = frame->sp;
frame_csp = frame->csp;
goto call_func_from_entry;
#if WASM_ENABLE_DEBUG_INTERP != 0
HANDLE_OP (DEBUG_OP_BREAK):
{
wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP);
exec_env->suspend_flags.flags |= 2;
frame_ip--;
SYNC_ALL_TO_FRAME();
CHECK_SUSPEND_FLAGS();
HANDLE_OP_END ();
}
#endif
#if WASM_ENABLE_LABELS_AS_VALUES == 0
default:
wasm_set_exception(module, "unsupported opcode");
@ -3320,6 +3388,9 @@ label_pop_csp_n:
PUSH_CSP(LABEL_TYPE_FUNCTION, cell_num, frame_ip_end - 1);
wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)frame);
#if WASM_ENABLE_THREAD_MGR != 0
CHECK_SUSPEND_FLAGS();
#endif
}
HANDLE_OP_END ();
}

View File

@ -10,6 +10,9 @@
#include "wasm_opcode.h"
#include "wasm_runtime.h"
#include "../common/wasm_native.h"
#if WASM_ENABLE_DEBUG_INTERP != 0
#include "../libraries/debug-engine/debug_engine.h"
#endif
/* Read a value of given type from the address pointed to by the given
pointer and increase the pointer to the position just after the
@ -2934,6 +2937,10 @@ load_from_sections(WASMModule *module, WASMSection *sections,
if (section->section_type == SECTION_TYPE_CODE) {
buf_code = section->section_body;
buf_code_end = buf_code + section->section_body_size;
#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_DEBUG_AOT != 0
module->buf_code = (uint8 *)buf_code;
module->buf_code_size = section->section_body_size;
#endif
}
else if (section->section_type == SECTION_TYPE_FUNC) {
buf_func = section->section_body;
@ -3296,10 +3303,26 @@ create_module(char *error_buf, uint32 error_buf_size)
#if WASM_ENABLE_MULTI_MODULE != 0
module->import_module_list = &module->import_module_list_head;
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
bh_list_init(&module->fast_opcode_list);
#endif
return module;
}
#if WASM_ENABLE_DEBUG_INTERP != 0
static void
record_fast_op(WASMModule *module, uint8 * pos, uint8 orig_op)
{
WASMFastOPCodeNode *fast_op = wasm_runtime_malloc(sizeof(WASMFastOPCodeNode));
if (fast_op) {
fast_op->offset = pos - module->load_addr;
fast_op->orig_op = orig_op;
bh_list_insert(&module->fast_opcode_list, fast_op);
}
}
#endif
WASMModule *
wasm_loader_load_from_sections(WASMSection *section_list,
char *error_buf, uint32 error_buf_size)
@ -3493,6 +3516,11 @@ wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_bu
return NULL;
}
#if WASM_ENABLE_DEBUG_INTERP != 0
module->load_addr = (uint8 *)buf;
module->load_size = size;
#endif
if (!load(buf, size, module, error_buf, error_buf_size)) {
goto fail;
}
@ -3593,7 +3621,6 @@ wasm_loader_unload(WASMModule *module)
*/
wasm_runtime_free(node);
/*
*
* the module file reading buffer will be released
* in runtime_destroy()
*/
@ -3601,12 +3628,21 @@ wasm_loader_unload(WASMModule *module)
}
}
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
WASMFastOPCodeNode *fast_opcode =
bh_list_first_elem(&module->fast_opcode_list);
while(fast_opcode) {
WASMFastOPCodeNode * next = bh_list_elem_next(fast_opcode);
wasm_runtime_free(fast_opcode);
fast_opcode = next;
}
#endif
wasm_runtime_free(module);
}
bool
wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
wasm_loader_find_block_addr(WASMExecEnv *exec_env,
BlockAddr *block_addr_cache,
const uint8 *start_addr,
const uint8 *code_end_addr,
uint8 label_type,
@ -3638,7 +3674,9 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
while (p < code_end_addr) {
opcode = *p++;
#if WASM_ENABLE_DEBUG_INTERP != 0
op_break_retry:
#endif
switch (opcode) {
case WASM_OP_UNREACHABLE:
case WASM_OP_NOP:
@ -4156,6 +4194,31 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
break;
}
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
case DEBUG_OP_BREAK: {
WASMDebugInstance *debug_instance =
wasm_exec_env_get_instance(exec_env);
char orignal_opcode[1];
uint64 size = 1;
WASMModuleInstance *module_inst =
(WASMModuleInstance *)exec_env->module_inst;
uint64 offset = (p - 1) >= module_inst->module->load_addr
? (p - 1) - module_inst->module->load_addr
: ~0;
if (debug_instance) {
if (wasm_debug_instance_get_obj_mem(
debug_instance, offset, orignal_opcode, &size)
&& size == 1) {
LOG_VERBOSE("WASM loader find OP_BREAK , recover it "
"with %02x: ",
orignal_opcode[0]);
opcode = orignal_opcode[0];
goto op_break_retry;
}
}
break;
}
#endif
default:
return false;
@ -6317,6 +6380,9 @@ handle_op_block_and_loop:
* to new extended opcode so that interpreter can resolve the
* block quickly.
*/
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p - 2, *(p - 2));
#endif
*(p - 2) = EXT_OP_BLOCK + (opcode - WASM_OP_BLOCK);
#endif
}
@ -7196,13 +7262,28 @@ handle_op_block_and_loop:
#else
#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
if (local_offset < 0x80) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = EXT_OP_GET_LOCAL_FAST;
if (is_32bit_type(local_type))
if (is_32bit_type(local_type)) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = (uint8)local_offset;
else
}
else {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = (uint8)(local_offset | 0x80);
while (p_org < p)
}
while (p_org < p) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = WASM_OP_NOP;
}
}
#endif
#endif
@ -7254,13 +7335,28 @@ handle_op_block_and_loop:
#else
#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
if (local_offset < 0x80) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = EXT_OP_SET_LOCAL_FAST;
if (is_32bit_type(local_type))
if (is_32bit_type(local_type)) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = (uint8)local_offset;
else
}
else {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = (uint8)(local_offset | 0x80);
while (p_org < p)
}
while (p_org < p) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = WASM_OP_NOP;
}
}
#endif
#endif
@ -7309,13 +7405,28 @@ handle_op_block_and_loop:
#else
#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
if (local_offset < 0x80) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = EXT_OP_TEE_LOCAL_FAST;
if (is_32bit_type(local_type))
if (is_32bit_type(local_type)) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = (uint8)local_offset;
else
}
else {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = (uint8)(local_offset | 0x80);
while (p_org < p)
}
while (p_org < p) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org++ = WASM_OP_NOP;
}
}
#endif
#endif
@ -7344,6 +7455,9 @@ handle_op_block_and_loop:
#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
if (global_type == VALUE_TYPE_I64
|| global_type == VALUE_TYPE_F64) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org = WASM_OP_GET_GLOBAL_64;
}
#endif
@ -7393,10 +7507,16 @@ handle_op_block_and_loop:
#if WASM_ENABLE_FAST_INTERP == 0
if (global_type == VALUE_TYPE_I64
|| global_type == VALUE_TYPE_F64) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org = WASM_OP_SET_GLOBAL_64;
}
else if (module->aux_stack_size > 0
&& global_idx == module->aux_stack_top_global_index) {
#if WASM_ENABLE_DEBUG_INTERP != 0
record_fast_op(module, p_org, *p_org);
#endif
*p_org = WASM_OP_SET_GLOBAL_AUX_STACK;
}
#else /* else of WASM_ENABLE_FAST_INTERP */

View File

@ -62,8 +62,10 @@ wasm_loader_unload(WASMModule *module);
*
* @return true if success, false otherwise
*/
bool
wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
wasm_loader_find_block_addr(WASMExecEnv *exec_env,
BlockAddr *block_addr_cache,
const uint8 *start_addr,
const uint8 *code_end_addr,
uint8 block_type,

View File

@ -267,6 +267,10 @@ typedef enum WASMOpcode {
EXT_OP_LOOP = 0xd4, /* loop with blocktype */
EXT_OP_IF = 0xd5, /* if with blocktype */
#if WASM_ENABLE_DEBUG_INTERP != 0
DEBUG_OP_BREAK = 0xd6, /* debug break point */
#endif
/* Post-MVP extend op prefix */
WASM_OP_MISC_PREFIX = 0xfc,
WASM_OP_SIMD_PREFIX = 0xfd,
@ -673,6 +677,14 @@ typedef enum WASMAtomicEXTOpcode {
}
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
#define DEF_DEBUG_BREAK_HANDLE(_name) \
_name[DEBUG_OP_BREAK] = \
HANDLE_OPCODE (DEBUG_OP_BREAK); /* 0xd6 */
#else
#define DEF_DEBUG_BREAK_HANDLE(_name)
#endif
/*
* Macro used to generate computed goto tables for the C interpreter.
*/
@ -900,6 +912,7 @@ do { \
HANDLE_OPCODE (WASM_OP_MISC_PREFIX); /* 0xfc */ \
_name[WASM_OP_ATOMIC_PREFIX] = \
HANDLE_OPCODE (WASM_OP_ATOMIC_PREFIX); /* 0xfe */ \
DEF_DEBUG_BREAK_HANDLE(_name) \
} while (0)
#endif /* end of _WASM_OPCODE_H */

View File

@ -16,6 +16,9 @@
#if WASM_ENABLE_THREAD_MGR != 0
#include "../libraries/thread-mgr/thread_manager.h"
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
#include "../libraries/debug-engine/debug_engine.h"
#endif
static void
set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
@ -1826,14 +1829,30 @@ wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size,
}
else if (module_inst->malloc_function
&& module_inst->free_function) {
#if WASM_ENABLE_DEBUG_INTERP != 0
/* TODO: obviously, we can not create debug instance for
* module malloc here, so, just disable the engine here,
* it is strange, but we now are lack of ways to indicate
* which calls should not be debugged. And we have other
* execute_xxx_function may need to be taken care of
*/
bool active = wasm_debug_get_engine_active();
wasm_debug_set_engine_active(false);
#endif
if (!execute_malloc_function(module_inst,
module_inst->malloc_function,
module_inst->retain_function,
size, &offset)) {
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_debug_set_engine_active(active);
#endif
return 0;
}
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_debug_set_engine_active(active);
#endif
/* If we use app's malloc function,
the default memory may be changed while memory growing */
the default memory may be changed while memory growing */
memory = module_inst->default_memory;
addr = offset ? memory->memory_data + offset : NULL;
}
@ -1915,9 +1934,19 @@ wasm_module_free(WASMModuleInstance *module_inst, uint32 ptr)
&& module_inst->free_function
&& memory->memory_data <= addr
&& addr < memory->memory_data_end) {
#if WASM_ENABLE_DEBUG_INTERP != 0
/*TODO: obviously, we can not create debug instance for module malloc here,
so, just disable the engine here, it is strange. the wasm's call should be
marshed to its own thread */
bool active = wasm_debug_get_engine_active();
wasm_debug_set_engine_active(false);
#endif
execute_free_function(module_inst,
module_inst->free_function,
ptr);
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_debug_set_engine_active(active);
#endif
}
}
}