Add memory watchpoint support for source debugger (#1762)

Allow to add watchpoints to variables for source debugging. For instance:
`breakpoint set variable var`
will pause WAMR execution when the address at var is written to.
Can also set read/write watchpoints by passing r/w flags. This will pause
execution when the address at var is read:
`watchpoint set variable -w read var`

Add two linked lists for read/write watchpoints. When the debug message
handler receives a watchpoint request, it adds/removes to one/both of these
lists. In the interpreter, when an address is read or stored to, check whether
the address is in these lists. If so, throw a sigtrap and suspend the process.
This commit is contained in:
Callum Macmillan
2022-12-07 09:18:28 +00:00
committed by GitHub
parent 9d52960e4d
commit c3d66f916e
4 changed files with 265 additions and 25 deletions

View File

@ -392,6 +392,8 @@ wasm_debug_instance_create(WASMCluster *cluster, int32 port)
}
bh_list_init(&instance->break_point_list);
bh_list_init(&instance->watch_point_list_read);
bh_list_init(&instance->watch_point_list_write);
instance->cluster = cluster;
exec_env = bh_list_first_elem(&cluster->exec_env_list);
@ -452,6 +454,23 @@ wasm_debug_instance_destroy_breakpoints(WASMDebugInstance *instance)
}
}
static void
wasm_debug_instance_destroy_watchpoints(WASMDebugInstance *instance,
bh_list *watchpoints)
{
WASMDebugWatchPoint *watchpoint, *next;
watchpoint = bh_list_first_elem(watchpoints);
while (watchpoint) {
next = bh_list_elem_next(watchpoint);
bh_list_remove(watchpoints, watchpoint);
wasm_runtime_free(watchpoint);
watchpoint = next;
}
}
void
wasm_debug_instance_destroy(WASMCluster *cluster)
{
@ -472,6 +491,10 @@ wasm_debug_instance_destroy(WASMCluster *cluster)
/* destroy all breakpoints */
wasm_debug_instance_destroy_breakpoints(instance);
wasm_debug_instance_destroy_watchpoints(
instance, &instance->watch_point_list_read);
wasm_debug_instance_destroy_watchpoints(
instance, &instance->watch_point_list_write);
os_mutex_destroy(&instance->wait_lock);
os_cond_destroy(&instance->wait_cond);
@ -995,6 +1018,65 @@ wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr,
return true;
}
static bool
add_watchpoint(bh_list *list, uint64 addr, uint64 length)
{
WASMDebugWatchPoint *watchpoint;
if (!(watchpoint = wasm_runtime_malloc(sizeof(WASMDebugWatchPoint)))) {
LOG_ERROR("WASM Debug Engine error: failed to allocate memory for "
"watchpoint");
return false;
}
memset(watchpoint, 0, sizeof(WASMDebugWatchPoint));
watchpoint->addr = addr;
watchpoint->length = length;
bh_list_insert(list, watchpoint);
return true;
}
static bool
remove_watchpoint(bh_list *list, uint64 addr, uint64 length)
{
WASMDebugWatchPoint *watchpoint = bh_list_first_elem(list);
while (watchpoint) {
WASMDebugWatchPoint *next = bh_list_elem_next(watchpoint);
if (watchpoint->addr == addr && watchpoint->length == length) {
bh_list_remove(list, watchpoint);
wasm_runtime_free(watchpoint);
}
watchpoint = next;
}
return true;
}
bool
wasm_debug_instance_watchpoint_write_add(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return add_watchpoint(&instance->watch_point_list_write, addr, length);
}
bool
wasm_debug_instance_watchpoint_write_remove(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return remove_watchpoint(&instance->watch_point_list_write, addr, length);
}
bool
wasm_debug_instance_watchpoint_read_add(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return add_watchpoint(&instance->watch_point_list_read, addr, length);
}
bool
wasm_debug_instance_watchpoint_read_remove(WASMDebugInstance *instance,
uint64 addr, uint64 length)
{
return remove_watchpoint(&instance->watch_point_list_read, addr, length);
}
bool
wasm_debug_instance_on_failure(WASMDebugInstance *instance)
{

View File

@ -36,6 +36,12 @@ typedef struct WASMDebugBreakPoint {
uint64 orignal_data;
} WASMDebugBreakPoint;
typedef struct WASMDebugWatchPoint {
bh_list_link next;
uint64 addr;
uint64 length;
} WASMDebugWatchPoint;
typedef enum debug_state_t {
/* Debugger state conversion sequence:
* DBG_LAUNCHING ---> APP_STOPPED <---> APP_RUNNING
@ -56,6 +62,8 @@ struct WASMDebugInstance {
struct WASMDebugInstance *next;
WASMDebugControlThread *control_thread;
bh_list break_point_list;
bh_list watch_point_list_read;
bh_list watch_point_list_write;
WASMCluster *cluster;
uint32 id;
korp_tid current_tid;
@ -184,6 +192,22 @@ bool
wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr,
uint64 length);
bool
wasm_debug_instance_watchpoint_write_add(WASMDebugInstance *instance,
uint64 addr, uint64 length);
bool
wasm_debug_instance_watchpoint_write_remove(WASMDebugInstance *instance,
uint64 addr, uint64 length);
bool
wasm_debug_instance_watchpoint_read_add(WASMDebugInstance *instance,
uint64 addr, uint64 length);
bool
wasm_debug_instance_watchpoint_read_remove(WASMDebugInstance *instance,
uint64 addr, uint64 length);
bool
wasm_debug_instance_on_failure(WASMDebugInstance *instance);

View File

@ -358,6 +358,14 @@ handle_general_query(WASMGDBServer *server, char *payload)
send_thread_stop_status(server, status, tid);
}
if (!strcmp(name, "WatchpointSupportInfo")) {
os_mutex_lock(&tmpbuf_lock);
// Any uint32 is OK for the watchpoint support
snprintf(tmpbuf, MAX_PACKET_SIZE, "num:32;");
write_packet(server, tmpbuf);
os_mutex_unlock(&tmpbuf_lock);
}
}
void
@ -643,46 +651,125 @@ handle_get_write_memory(WASMGDBServer *server, char *payload)
os_mutex_unlock(&tmpbuf_lock);
}
void
handle_breakpoint_software_add(WASMGDBServer *server, uint64 addr,
size_t length)
{
bool ret = wasm_debug_instance_add_breakpoint(
(WASMDebugInstance *)server->thread->debug_instance, addr, length);
write_packet(server, ret ? "OK" : "EO1");
}
void
handle_breakpoint_software_remove(WASMGDBServer *server, uint64 addr,
size_t length)
{
bool ret = wasm_debug_instance_remove_breakpoint(
(WASMDebugInstance *)server->thread->debug_instance, addr, length);
write_packet(server, ret ? "OK" : "EO1");
}
void
handle_watchpoint_write_add(WASMGDBServer *server, uint64 addr, size_t length)
{
bool ret = wasm_debug_instance_watchpoint_write_add(
(WASMDebugInstance *)server->thread->debug_instance, addr, length);
write_packet(server, ret ? "OK" : "EO1");
}
void
handle_watchpoint_write_remove(WASMGDBServer *server, uint64 addr,
size_t length)
{
bool ret = wasm_debug_instance_watchpoint_write_remove(
(WASMDebugInstance *)server->thread->debug_instance, addr, length);
write_packet(server, ret ? "OK" : "EO1");
}
void
handle_watchpoint_read_add(WASMGDBServer *server, uint64 addr, size_t length)
{
bool ret = wasm_debug_instance_watchpoint_read_add(
(WASMDebugInstance *)server->thread->debug_instance, addr, length);
write_packet(server, ret ? "OK" : "EO1");
}
void
handle_watchpoint_read_remove(WASMGDBServer *server, uint64 addr, size_t length)
{
bool ret = wasm_debug_instance_watchpoint_read_remove(
(WASMDebugInstance *)server->thread->debug_instance, addr, length);
write_packet(server, ret ? "OK" : "EO1");
}
void
handle_add_break(WASMGDBServer *server, char *payload)
{
int arg_c;
size_t type, length;
uint64 addr;
if (sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length) == 3) {
if (type == eBreakpointSoftware) {
bool ret = wasm_debug_instance_add_breakpoint(
(WASMDebugInstance *)server->thread->debug_instance, addr,
length);
if (ret)
write_packet(server, "OK");
else
write_packet(server, "E01");
return;
}
if ((arg_c = sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length))
!= 3) {
LOG_ERROR("Unsupported number of add break arguments %d", arg_c);
write_packet(server, "");
return;
}
switch (type) {
case eBreakpointSoftware:
handle_breakpoint_software_add(server, addr, length);
break;
case eWatchpointWrite:
handle_watchpoint_write_add(server, addr, length);
break;
case eWatchpointRead:
handle_watchpoint_read_add(server, addr, length);
break;
case eWatchpointReadWrite:
handle_watchpoint_write_add(server, addr, length);
handle_watchpoint_read_add(server, addr, length);
break;
default:
LOG_ERROR("Unsupported breakpoint type %d", type);
write_packet(server, "");
break;
}
write_packet(server, "");
}
void
handle_remove_break(WASMGDBServer *server, char *payload)
{
int arg_c;
size_t type, length;
uint64 addr;
if (sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length) == 3) {
if (type == eBreakpointSoftware) {
bool ret = wasm_debug_instance_remove_breakpoint(
(WASMDebugInstance *)server->thread->debug_instance, addr,
length);
if (ret)
write_packet(server, "OK");
else
write_packet(server, "E01");
return;
}
if ((arg_c = sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length))
!= 3) {
LOG_ERROR("Unsupported number of remove break arguments %d", arg_c);
write_packet(server, "");
return;
}
switch (type) {
case eBreakpointSoftware:
handle_breakpoint_software_remove(server, addr, length);
break;
case eWatchpointWrite:
handle_watchpoint_write_remove(server, addr, length);
break;
case eWatchpointRead:
handle_watchpoint_read_remove(server, addr, length);
break;
case eWatchpointReadWrite:
handle_watchpoint_write_remove(server, addr, length);
handle_watchpoint_read_remove(server, addr, length);
break;
default:
LOG_ERROR("Unsupported breakpoint type %d", type);
write_packet(server, "");
break;
}
write_packet(server, "");
}
void