Bladeren bron

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.
Callum Macmillan 3 jaren geleden
bovenliggende
commit
c3d66f916e

+ 48 - 1
core/iwasm/interpreter/wasm_interp_classic.c

@@ -70,6 +70,35 @@ typedef float64 CellType_F64;
             goto unaligned_atomic;                                   \
             goto unaligned_atomic;                                   \
     } while (0)
     } while (0)
 
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#define TRIGGER_WATCHPOINT_SIGTRAP()                              \
+    do {                                                          \
+        wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP); \
+        CHECK_SUSPEND_FLAGS();                                    \
+    } while (0)
+
+#define CHECK_WATCHPOINT(list, current_addr)                               \
+    do {                                                                   \
+        WASMDebugWatchPoint *watchpoint = bh_list_first_elem(list);        \
+        while (watchpoint) {                                               \
+            WASMDebugWatchPoint *next = bh_list_elem_next(watchpoint);     \
+            if (watchpoint->addr <= current_addr                           \
+                && watchpoint->addr + watchpoint->length > current_addr) { \
+                TRIGGER_WATCHPOINT_SIGTRAP();                              \
+            }                                                              \
+            watchpoint = next;                                             \
+        }                                                                  \
+    } while (0)
+
+#define CHECK_READ_WATCHPOINT(addr, offset) \
+    CHECK_WATCHPOINT(watch_point_list_read, WASM_ADDR_OFFSET(addr + offset))
+#define CHECK_WRITE_WATCHPOINT(addr, offset) \
+    CHECK_WATCHPOINT(watch_point_list_write, WASM_ADDR_OFFSET(addr + offset))
+#else
+#define CHECK_READ_WATCHPOINT(addr, offset)
+#define CHECK_WRITE_WATCHPOINT(addr, offset)
+#endif
+
 static inline uint32
 static inline uint32
 rotl32(uint32 n, uint32 c)
 rotl32(uint32 n, uint32 c)
 {
 {
@@ -1127,6 +1156,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
 
 
 #if WASM_ENABLE_DEBUG_INTERP != 0
 #if WASM_ENABLE_DEBUG_INTERP != 0
     uint8 *frame_ip_orig = NULL;
     uint8 *frame_ip_orig = NULL;
+    WASMDebugInstance *debug_instance = wasm_exec_env_get_instance(exec_env);
+    bh_list *watch_point_list_read = &debug_instance->watch_point_list_read;
+    bh_list *watch_point_list_write = &debug_instance->watch_point_list_write;
 #endif
 #endif
 
 
 #if WASM_ENABLE_LABELS_AS_VALUES != 0
 #if WASM_ENABLE_LABELS_AS_VALUES != 0
@@ -1792,6 +1824,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(4);
                 CHECK_MEMORY_OVERFLOW(4);
                 PUSH_I32(LOAD_I32(maddr));
                 PUSH_I32(LOAD_I32(maddr));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1806,6 +1839,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(8);
                 CHECK_MEMORY_OVERFLOW(8);
                 PUSH_I64(LOAD_I64(maddr));
                 PUSH_I64(LOAD_I64(maddr));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1819,6 +1853,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(1);
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I32(sign_ext_8_32(*(int8 *)maddr));
                 PUSH_I32(sign_ext_8_32(*(int8 *)maddr));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1832,6 +1867,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(1);
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I32((uint32)(*(uint8 *)maddr));
                 PUSH_I32((uint32)(*(uint8 *)maddr));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1845,6 +1881,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(2);
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I32(sign_ext_16_32(LOAD_I16(maddr)));
                 PUSH_I32(sign_ext_16_32(LOAD_I16(maddr)));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1858,6 +1895,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(2);
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I32((uint32)(LOAD_U16(maddr)));
                 PUSH_I32((uint32)(LOAD_U16(maddr)));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1871,6 +1909,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(1);
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I64(sign_ext_8_64(*(int8 *)maddr));
                 PUSH_I64(sign_ext_8_64(*(int8 *)maddr));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1884,6 +1923,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(1);
                 CHECK_MEMORY_OVERFLOW(1);
                 PUSH_I64((uint64)(*(uint8 *)maddr));
                 PUSH_I64((uint64)(*(uint8 *)maddr));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1897,6 +1937,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(2);
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I64(sign_ext_16_64(LOAD_I16(maddr)));
                 PUSH_I64(sign_ext_16_64(LOAD_I16(maddr)));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1910,6 +1951,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(2);
                 CHECK_MEMORY_OVERFLOW(2);
                 PUSH_I64((uint64)(LOAD_U16(maddr)));
                 PUSH_I64((uint64)(LOAD_U16(maddr)));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1924,6 +1966,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(4);
                 CHECK_MEMORY_OVERFLOW(4);
                 PUSH_I64(sign_ext_32_64(LOAD_I32(maddr)));
                 PUSH_I64(sign_ext_32_64(LOAD_I32(maddr)));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1937,6 +1980,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(4);
                 CHECK_MEMORY_OVERFLOW(4);
                 PUSH_I64((uint64)(LOAD_U32(maddr)));
                 PUSH_I64((uint64)(LOAD_U32(maddr)));
+                CHECK_READ_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1953,6 +1997,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 addr = POP_I32();
                 addr = POP_I32();
                 CHECK_MEMORY_OVERFLOW(4);
                 CHECK_MEMORY_OVERFLOW(4);
                 STORE_U32(maddr, frame_sp[1]);
                 STORE_U32(maddr, frame_sp[1]);
+                CHECK_WRITE_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1969,6 +2014,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                 CHECK_MEMORY_OVERFLOW(8);
                 CHECK_MEMORY_OVERFLOW(8);
                 PUT_I64_TO_ADDR((uint32 *)maddr,
                 PUT_I64_TO_ADDR((uint32 *)maddr,
                                 GET_I64_FROM_ADDR(frame_sp + 1));
                                 GET_I64_FROM_ADDR(frame_sp + 1));
+                CHECK_WRITE_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -1993,7 +2039,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     CHECK_MEMORY_OVERFLOW(2);
                     CHECK_MEMORY_OVERFLOW(2);
                     STORE_U16(maddr, (uint16)sval);
                     STORE_U16(maddr, (uint16)sval);
                 }
                 }
-
+                CHECK_WRITE_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }
@@ -2023,6 +2069,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
                     CHECK_MEMORY_OVERFLOW(4);
                     CHECK_MEMORY_OVERFLOW(4);
                     STORE_U32(maddr, (uint32)sval);
                     STORE_U32(maddr, (uint32)sval);
                 }
                 }
+                CHECK_WRITE_WATCHPOINT(addr, offset);
                 (void)flags;
                 (void)flags;
                 HANDLE_OP_END();
                 HANDLE_OP_END();
             }
             }

+ 82 - 0
core/iwasm/libraries/debug-engine/debug_engine.c

@@ -392,6 +392,8 @@ wasm_debug_instance_create(WASMCluster *cluster, int32 port)
     }
     }
 
 
     bh_list_init(&instance->break_point_list);
     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;
     instance->cluster = cluster;
     exec_env = bh_list_first_elem(&cluster->exec_env_list);
     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
 void
 wasm_debug_instance_destroy(WASMCluster *cluster)
 wasm_debug_instance_destroy(WASMCluster *cluster)
 {
 {
@@ -472,6 +491,10 @@ wasm_debug_instance_destroy(WASMCluster *cluster)
 
 
         /* destroy all breakpoints */
         /* destroy all breakpoints */
         wasm_debug_instance_destroy_breakpoints(instance);
         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_mutex_destroy(&instance->wait_lock);
         os_cond_destroy(&instance->wait_cond);
         os_cond_destroy(&instance->wait_cond);
@@ -995,6 +1018,65 @@ wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr,
     return true;
     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
 bool
 wasm_debug_instance_on_failure(WASMDebugInstance *instance)
 wasm_debug_instance_on_failure(WASMDebugInstance *instance)
 {
 {

+ 24 - 0
core/iwasm/libraries/debug-engine/debug_engine.h

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

+ 111 - 24
core/iwasm/libraries/debug-engine/handler.c

@@ -358,6 +358,14 @@ handle_general_query(WASMGDBServer *server, char *payload)
 
 
         send_thread_stop_status(server, status, tid);
         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
 void
@@ -643,46 +651,125 @@ handle_get_write_memory(WASMGDBServer *server, char *payload)
     os_mutex_unlock(&tmpbuf_lock);
     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
 void
 handle_add_break(WASMGDBServer *server, char *payload)
 handle_add_break(WASMGDBServer *server, char *payload)
 {
 {
+    int arg_c;
     size_t type, length;
     size_t type, length;
     uint64 addr;
     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
 void
 handle_remove_break(WASMGDBServer *server, char *payload)
 handle_remove_break(WASMGDBServer *server, char *payload)
 {
 {
+    int arg_c;
     size_t type, length;
     size_t type, length;
     uint64 addr;
     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
 void