Эх сурвалжийг харах

[debugger enhance] don't block gdbserver thread while executing (#989)

Allow to set break point again when all break points are removed and
wasm app starts running.
Xu Jun 4 жил өмнө
parent
commit
3fe191b0df

+ 7 - 1
build-scripts/runtime_lib.cmake

@@ -87,6 +87,12 @@ endif ()
 if (WAMR_BUILD_DEBUG_INTERP EQUAL 1)
     set (WAMR_BUILD_THREAD_MGR 1)
     include (${IWASM_DIR}/libraries/debug-engine/debug_engine.cmake)
+
+    if (WAMR_BUILD_FAST_INTERP EQUAL 1)
+        set (WAMR_BUILD_FAST_INTERP 0)
+        message(STATUS
+                "Debugger doesn't work with fast interpreter, switch to classic interpreter")
+    endif ()
 endif ()
 
 if (WAMR_BUILD_THREAD_MGR EQUAL 1)
@@ -95,7 +101,7 @@ endif ()
 
 if (WAMR_BUILD_LIBC_EMCC EQUAL 1)
     include (${IWASM_DIR}/libraries/libc-emcc/libc_emcc.cmake)
-endif()
+endif ()
 
 ####################### Common sources #######################
 if (NOT MSVC)

+ 3 - 1
core/iwasm/common/wasm_exec_env.c

@@ -160,10 +160,12 @@ wasm_exec_env_destroy(WASMExecEnv *exec_env)
     /* Terminate all sub-threads */
     WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
     if (cluster) {
+        wasm_cluster_terminate_all_except_self(cluster, exec_env);
 #if WASM_ENABLE_DEBUG_INTERP != 0
+        /* Must fire exit event after other threads exits, otherwise
+           the stopped thread will be overrided by other threads */
         wasm_cluster_thread_exited(exec_env);
 #endif
-        wasm_cluster_terminate_all_except_self(cluster, exec_env);
         wasm_cluster_del_exec_env(cluster, exec_env);
     }
 #endif /* end of WASM_ENABLE_THREAD_MGR */

+ 107 - 42
core/iwasm/libraries/debug-engine/debug_engine.c

@@ -24,6 +24,20 @@ typedef struct WASMDebugEngine {
     bool active;
 } 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;
@@ -104,6 +118,50 @@ control_thread_routine(void *arg)
     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;
             }
@@ -148,8 +206,10 @@ wasm_debug_control_thread_create(WASMDebugInstance *debug_instance)
     /* 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)
+    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 */
@@ -467,46 +527,6 @@ wasm_debug_instance_get_tids(WASMDebugInstance *instance, korp_tid tids[],
     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;
-}
-
-korp_tid
-wasm_debug_instance_wait_thread(WASMDebugInstance *instance, korp_tid tid,
-                                uint32 *status)
-{
-    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 = (uint32)exec_env->current_status->signal_flag;
-    return exec_env->handle;
-}
-
 uint32
 wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, korp_tid tid)
 {
@@ -538,7 +558,8 @@ wasm_debug_instance_get_pc(WASMDebugInstance *instance)
         return 0;
 
     exec_env = wasm_debug_instance_get_current_env(instance);
-    if ((exec_env->cur_frame != NULL) && (exec_env->cur_frame->ip != NULL)) {
+    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(
@@ -935,6 +956,11 @@ wasm_debug_instance_continue(WASMDebugInstance *instance)
     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;
@@ -943,6 +969,28 @@ wasm_debug_instance_continue(WASMDebugInstance *instance)
         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;
 }
 
@@ -960,8 +1008,17 @@ wasm_debug_instance_kill(WASMDebugInstance *instance)
 
     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;
 }
 
@@ -973,6 +1030,11 @@ wasm_debug_instance_singlestep(WASMDebugInstance *instance, korp_tid tid)
     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;
@@ -984,6 +1046,9 @@ wasm_debug_instance_singlestep(WASMDebugInstance *instance, korp_tid tid)
         }
         exec_env = bh_list_elem_next(exec_env);
     }
+
+    instance->current_state = APP_RUNNING;
+
     return true;
 }
 

+ 22 - 4
core/iwasm/libraries/debug-engine/debug_engine.h

@@ -35,6 +35,15 @@ typedef struct WASMDebugBreakPoint {
     uint64 orignal_data;
 } WASMDebugBreakPoint;
 
+typedef enum debug_state_t {
+    /* Debugger state conversion sequence:
+     *   DBG_LAUNCHING ---> APP_STOPPED <---> APP_RUNNING
+     */
+    DBG_LAUNCHING,
+    APP_RUNNING,
+    APP_STOPPED
+} debug_state_t;
+
 typedef struct WASMDebugInstance {
     struct WASMDebugInstance *next;
     WASMDebugControlThread *control_thread;
@@ -44,6 +53,13 @@ typedef struct WASMDebugInstance {
     korp_tid current_tid;
     korp_mutex wait_lock;
     korp_cond wait_cond;
+    /* Last stopped thread, it should be set to NULL when sending
+     * out the thread stop reply */
+    WASMExecEnv *volatile stopped_thread;
+    /* Currently status of the debug instance, it will be set to
+     * RUNNING when receiving STEP/CONTINUE commands, and set to
+     * STOPPED when any thread stopped */
+    volatile debug_state_t current_state;
 } WASMDebugInstance;
 
 typedef enum WASMDebugEventKind {
@@ -77,6 +93,9 @@ typedef enum WasmAddressType {
 
 #define INVALIED_ADDR (0xFFFFFFFFFFFFFFFF)
 
+void
+on_thread_stop_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env);
+
 WASMDebugInstance *
 wasm_debug_instance_create(WASMCluster *cluster);
 
@@ -152,16 +171,15 @@ bool
 wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr,
                                       uint64 length);
 
+bool
+wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance);
+
 bool
 wasm_debug_instance_continue(WASMDebugInstance *instance);
 
 bool
 wasm_debug_instance_kill(WASMDebugInstance *instance);
 
-korp_tid
-wasm_debug_instance_wait_thread(WASMDebugInstance *instance, korp_tid tid,
-                                uint32 *status);
-
 uint32
 wasm_debug_instance_get_thread_status(WASMDebugInstance *instance,
                                       korp_tid tid);

+ 156 - 30
core/iwasm/libraries/debug-engine/gdbserver.c

@@ -51,6 +51,14 @@ wasm_create_gdbserver(const char *host, int32 *port)
 
     memset(server, 0, sizeof(WASMGDBServer));
 
+    if (!(server->receive_ctx =
+              wasm_runtime_malloc(sizeof(rsp_recv_context_t)))) {
+        LOG_ERROR("wasm gdb server error: failed to allocate memory");
+        goto fail;
+    }
+
+    memset(server->receive_ctx, 0, sizeof(rsp_recv_context_t));
+
     if (0 != os_socket_create(&listen_fd, 1)) {
         LOG_ERROR("wasm gdb server error: create socket failed");
         goto fail;
@@ -71,6 +79,8 @@ fail:
         os_socket_shutdown(listen_fd);
         os_socket_close(listen_fd);
     }
+    if (server->receive_ctx)
+        wasm_runtime_free(server->receive_ctx);
     if (server)
         wasm_runtime_free(server);
     return NULL;
@@ -108,6 +118,9 @@ fail:
 void
 wasm_close_gdbserver(WASMGDBServer *server)
 {
+    if (server->receive_ctx) {
+        wasm_runtime_free(server->receive_ctx);
+    }
     if (server->socket_fd > 0) {
         os_socket_shutdown(server->socket_fd);
         os_socket_close(server->socket_fd);
@@ -119,57 +132,170 @@ wasm_close_gdbserver(WASMGDBServer *server)
 }
 
 static inline void
-handler_packet(WASMGDBServer *server, char request, char *payload)
+handle_packet(WASMGDBServer *server, char request, char *payload)
 {
     if (packet_handler_table[(int)request].handler != NULL)
         packet_handler_table[(int)request].handler(server, payload);
 }
 
+static void
+process_packet(WASMGDBServer *server)
+{
+    uint8 *inbuf = (uint8 *)server->receive_ctx->receive_buffer;
+    char request;
+    char *payload = NULL;
+
+    request = inbuf[0];
+
+    if (request == '\0') {
+        LOG_VERBOSE("ignore empty request");
+        return;
+    }
+
+    payload = (char *)&inbuf[1];
+
+    LOG_VERBOSE("receive request:%c %s\n", request, payload);
+    handle_packet(server, request, payload);
+}
+
+static inline void
+push_byte(rsp_recv_context_t *ctx, unsigned char ch, bool checksum)
+{
+    if (ctx->receive_index >= sizeof(ctx->receive_buffer)) {
+        LOG_ERROR("RSP message buffer overflow");
+        bh_assert(false);
+        return;
+    }
+
+    ctx->receive_buffer[ctx->receive_index++] = ch;
+
+    if (checksum) {
+        ctx->check_sum += ch;
+    }
+}
+
 /**
  * The packet layout is:
+ * 1. Normal packet:
  *   '$' + payload + '#' + checksum(2bytes)
  *                    ^
- *                    packetend_ptr
+ *                    packetend
+ * 2. Interrupt:
+ *   0x03
  */
-static void
-process_packet(WASMGDBServer *server)
+
+/* return:
+ *  0: incomplete message received
+ *  1: complete message received
+ *  2: interrupt message received
+ */
+static int
+on_rsp_byte_arrive(unsigned char ch, rsp_recv_context_t *ctx)
 {
-    uint8 *inbuf = server->pkt.buf;
-    int32 inbuf_size = server->pkt.size;
-    uint8 *packetend_ptr = (uint8 *)memchr(inbuf, '#', inbuf_size);
-    int32 packet_size = (int32)(uintptr_t)(packetend_ptr - inbuf);
-    char request = inbuf[1];
-    char *payload = NULL;
-    uint8 checksum = 0;
+    if (ctx->phase == Phase_Idle) {
+        ctx->receive_index = 0;
+        ctx->check_sum = 0;
 
-    if (packet_size == 1) {
-        LOG_VERBOSE("receive empty request, ignore it\n");
-        return;
+        if (ch == 0x03) {
+            LOG_VERBOSE("Receive interrupt package");
+            return 2;
+        }
+        else if (ch == '$') {
+            ctx->phase = Phase_Payload;
+        }
+
+        return 0;
     }
+    else if (ctx->phase == Phase_Payload) {
+        if (ch == '#') {
+            ctx->phase = Phase_Checksum;
+            push_byte(ctx, ch, false);
+        }
+        else {
+            push_byte(ctx, ch, true);
+        }
 
-    bh_assert('$' == inbuf[0]);
-    inbuf[packet_size] = '\0';
+        return 0;
+    }
+    else if (ctx->phase == Phase_Checksum) {
+        ctx->size_in_phase++;
+        push_byte(ctx, ch, false);
 
-    for (int i = 1; i < packet_size; i++)
-        checksum += inbuf[i];
-    bh_assert(
-        checksum
-        == (hex(inbuf[packet_size + 1]) << 4 | hex(inbuf[packet_size + 2])));
+        if (ctx->size_in_phase == 2) {
+            ctx->size_in_phase = 0;
 
-    payload = (char *)&inbuf[2];
+            if ((hex(ctx->receive_buffer[ctx->receive_index - 2]) << 4
+                 | hex(ctx->receive_buffer[ctx->receive_index - 1]))
+                != ctx->check_sum) {
+                LOG_WARNING("RSP package checksum error, ignore it");
+                ctx->phase = Phase_Idle;
+                return 0;
+            }
+            else {
+                /* Change # to \0 */
+                ctx->receive_buffer[ctx->receive_index - 3] = '\0';
+                ctx->phase = Phase_Idle;
+                return 1;
+            }
+        }
 
-    LOG_VERBOSE("receive request:%c %s\n", request, payload);
-    handler_packet(server, request, payload);
+        return 0;
+    }
 
-    inbuf_erase_head(server, packet_size + 3);
+    /* Should never reach here */
+    bh_assert(false);
+    return 0;
 }
 
 bool
 wasm_gdbserver_handle_packet(WASMGDBServer *server)
 {
-    bool ret;
-    ret = read_packet(server);
-    if (ret)
-        process_packet(server);
-    return ret;
+    int32 n;
+    char buf[1024];
+
+    if (os_socket_settimeout(server->socket_fd, 1000) != 0) {
+        LOG_ERROR("Set socket recv timeout failed");
+        return false;
+    }
+
+    n = os_socket_recv(server->socket_fd, buf, sizeof(buf));
+
+    if (n == 0) {
+        LOG_VERBOSE("Debugger disconnected");
+        return false;
+    }
+    else if (n < 0) {
+#if defined(BH_PLATFORM_WINDOWS)
+        if (WSAGetLastError() == WSAETIMEDOUT)
+#else
+        if (errno == EAGAIN || errno == EWOULDBLOCK)
+#endif
+        {
+            /* No bytes arrived */
+            return true;
+        }
+        else {
+            LOG_ERROR("Socket receive error");
+            return false;
+        }
+    }
+    else {
+        int32 i, ret;
+
+        for (i = 0; i < n; i++) {
+            ret = on_rsp_byte_arrive(buf[i], server->receive_ctx);
+
+            if (ret == 1) {
+                if (!server->noack)
+                    write_data_raw(server, (uint8 *)"+", 1);
+
+                process_packet(server);
+            }
+            else if (ret == 2) {
+                handle_interrupt(server);
+            }
+        }
+    }
+
+    return true;
 }

+ 18 - 0
core/iwasm/libraries/debug-engine/gdbserver.h

@@ -18,6 +18,23 @@ enum GDBStoppointType {
     eWatchpointRead,
     eWatchpointReadWrite
 };
+
+typedef enum rsp_recv_phase_t {
+    Phase_Idle,
+    Phase_Payload,
+    Phase_Checksum
+} rsp_recv_phase_t;
+
+/* Remote Serial Protocol Receive Context */
+typedef struct rsp_recv_context_t {
+    rsp_recv_phase_t phase;
+    uint16 receive_index;
+    uint16 size_in_phase;
+    uint8 check_sum;
+    /* RSP packet should not be too long */
+    char receive_buffer[1024];
+} rsp_recv_context_t;
+
 typedef struct WasmDebugPacket {
     unsigned char buf[PACKET_BUF_SIZE];
     uint32 size;
@@ -30,6 +47,7 @@ typedef struct WASMGDBServer {
     WasmDebugPacket pkt;
     bool noack;
     struct WASMDebugControlThread *thread;
+    rsp_recv_context_t *receive_ctx;
 } WASMGDBServer;
 
 WASMGDBServer *

+ 40 - 45
core/iwasm/libraries/debug-engine/handler.c

@@ -26,8 +26,11 @@ wasm_debug_handler_deinit()
     os_mutex_destroy(&tmpbuf_lock);
 }
 
-static void
-send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid);
+void
+handle_interrupt(WASMGDBServer *server)
+{
+    wasm_debug_instance_interrupt_all_threads(server->thread->debug_instance);
+}
 
 void
 handle_generay_set(WASMGDBServer *server, char *payload)
@@ -91,7 +94,7 @@ process_xfer(WASMGDBServer *server, const char *name, char *args)
 }
 
 void
-porcess_wasm_local(WASMGDBServer *server, char *args)
+process_wasm_local(WASMGDBServer *server, char *args)
 {
     int32 frame_index;
     int32 local_index;
@@ -114,7 +117,7 @@ porcess_wasm_local(WASMGDBServer *server, char *args)
 }
 
 void
-porcess_wasm_global(WASMGDBServer *server, char *args)
+process_wasm_global(WASMGDBServer *server, char *args)
 {
     int32 frame_index;
     int32 global_index;
@@ -189,12 +192,12 @@ handle_generay_query(WASMGDBServer *server, char *payload)
     }
 
     if (!strcmp(name, "HostInfo")) {
-        // Todo: change vendor to Intel for outside tree?
-        mem2hex("wasm32-Ant-wasi-wasm", triple, strlen("wasm32-Ant-wasi-wasm"));
+        mem2hex("wasm32-wamr-wasi-wasm", triple,
+                strlen("wasm32-wamr-wasi-wasm"));
 
         os_mutex_lock(&tmpbuf_lock);
         snprintf(tmpbuf, sizeof(tmpbuf),
-                 "vendor:Ant;ostype:wasi;arch:wasm32;"
+                 "vendor:wamr;ostype:wasi;arch:wasm32;"
                  "triple:%s;endian:little;ptrsize:4;",
                  triple);
         write_packet(server, tmpbuf);
@@ -220,13 +223,13 @@ handle_generay_query(WASMGDBServer *server, char *payload)
         uint64 pid;
         pid = wasm_debug_instance_get_pid(
             (WASMDebugInstance *)server->thread->debug_instance);
-        // arch-vendor-os-env(format)
-        mem2hex("wasm32-Ant-wasi-wasm", triple, strlen("wasm32-Ant-wasi-wasm"));
+        mem2hex("wasm32-wamr-wasi-wasm", triple,
+                strlen("wasm32-wamr-wasi-wasm"));
 
         os_mutex_lock(&tmpbuf_lock);
         snprintf(tmpbuf, sizeof(tmpbuf),
                  "pid:%" PRIx64 ";parent-pid:%" PRIx64
-                 ";vendor:Ant;ostype:wasi;arch:wasm32;"
+                 ";vendor:wamr;ostype:wasi;arch:wasm32;"
                  "triple:%s;endian:little;ptrsize:4;",
                  pid, pid, triple);
         write_packet(server, tmpbuf);
@@ -298,11 +301,11 @@ handle_generay_query(WASMGDBServer *server, char *payload)
     }
 
     if (args && (!strcmp(name, "WasmLocal"))) {
-        porcess_wasm_local(server, args);
+        process_wasm_local(server, args);
     }
 
     if (args && (!strcmp(name, "WasmGlobal"))) {
-        porcess_wasm_global(server, args);
+        process_wasm_global(server, args);
     }
 
     if (!strcmp(name, "Offsets")) {
@@ -322,7 +325,7 @@ handle_generay_query(WASMGDBServer *server, char *payload)
     }
 }
 
-static void
+void
 send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid)
 {
     int32 len = 0;
@@ -391,7 +394,6 @@ handle_v_packet(WASMGDBServer *server, char *payload)
 {
     const char *name;
     char *args;
-    uint32 status;
 
     args = strchr(payload, ';');
     if (args)
@@ -427,11 +429,6 @@ handle_v_packet(WASMGDBServer *server, char *payload)
                             (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);
                 }
             }
         }
@@ -441,14 +438,34 @@ handle_v_packet(WASMGDBServer *server, char *payload)
 void
 handle_threadstop_request(WASMGDBServer *server, char *payload)
 {
-    korp_tid tid = wasm_debug_instance_get_tid(
-        (WASMDebugInstance *)server->thread->debug_instance);
+    korp_tid tid;
     uint32 status;
+    WASMDebugInstance *debug_inst =
+        (WASMDebugInstance *)server->thread->debug_instance;
+    bh_assert(debug_inst);
+
+    /* According to
+       https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#Packets, the "?"
+       package should be sent when connection is first established to query the
+       reason the target halted */
+    bh_assert(debug_inst->current_state == DBG_LAUNCHING);
 
-    tid = wasm_debug_instance_wait_thread(
-        (WASMDebugInstance *)server->thread->debug_instance, tid, &status);
+    /* Waiting for the stop event */
+    os_mutex_lock(&debug_inst->wait_lock);
+    while (!debug_inst->stopped_thread) {
+        os_cond_wait(&debug_inst->wait_cond, &debug_inst->wait_lock);
+    }
+    os_mutex_unlock(&debug_inst->wait_lock);
+
+    tid = debug_inst->stopped_thread->handle;
+    status = (uint32)debug_inst->stopped_thread->current_status->signal_flag;
+
+    wasm_debug_instance_set_cur_thread(debug_inst, tid);
 
     send_thread_stop_status(server, status, tid);
+
+    debug_inst->current_state = APP_STOPPED;
+    debug_inst->stopped_thread = NULL;
 }
 
 void
@@ -611,37 +628,15 @@ handle_remove_break(WASMGDBServer *server, char *payload)
 void
 handle_continue_request(WASMGDBServer *server, char *payload)
 {
-    korp_tid tid;
-    uint32 status;
-
     wasm_debug_instance_continue(
         (WASMDebugInstance *)server->thread->debug_instance);
-
-    tid = wasm_debug_instance_get_tid(
-        (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);
 }
 
 void
 handle_kill_request(WASMGDBServer *server, char *payload)
 {
-    korp_tid tid;
-    uint32 status;
-
     wasm_debug_instance_kill(
         (WASMDebugInstance *)server->thread->debug_instance);
-
-    tid = wasm_debug_instance_get_tid(
-        (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);
 }
 
 static void

+ 6 - 0
core/iwasm/libraries/debug-engine/handler.h

@@ -14,6 +14,9 @@ wasm_debug_handler_init();
 void
 wasm_debug_handler_deinit();
 
+void
+handle_interrupt(WASMGDBServer *server);
+
 void
 handle_generay_set(WASMGDBServer *server, char *payload);
 
@@ -58,4 +61,7 @@ handle_kill_request(WASMGDBServer *server, char *payload);
 
 void
 handle____request(WASMGDBServer *server, char *payload);
+
+void
+send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid);
 #endif

+ 0 - 87
core/iwasm/libraries/debug-engine/packets.c

@@ -7,56 +7,6 @@
 #include "packets.h"
 #include "gdbserver.h"
 
-void
-pktbuf_insert(WASMGDBServer *gdbserver, const uint8 *buf, ssize_t len)
-{
-    WasmDebugPacket *pkt = &gdbserver->pkt;
-
-    if ((unsigned long)(pkt->size + len) >= sizeof(pkt->buf)) {
-        LOG_ERROR("Packet buffer overflow");
-        exit(-2);
-    }
-
-    memcpy(pkt->buf + pkt->size, buf, len);
-    pkt->size += len;
-}
-
-void
-pktbuf_erase_head(WASMGDBServer *gdbserver, ssize_t index)
-{
-    WasmDebugPacket *pkt = &gdbserver->pkt;
-    memmove(pkt->buf, pkt->buf + index, pkt->size - index);
-    pkt->size -= index;
-}
-
-void
-inbuf_erase_head(WASMGDBServer *gdbserver, ssize_t index)
-{
-    pktbuf_erase_head(gdbserver, index);
-}
-
-void
-pktbuf_clear(WASMGDBServer *gdbserver)
-{
-    WasmDebugPacket *pkt = &gdbserver->pkt;
-    pkt->size = 0;
-}
-
-int32
-read_data_once(WASMGDBServer *gdbserver)
-{
-    ssize_t nread;
-    uint8 buf[4096];
-
-    nread = os_socket_recv(gdbserver->socket_fd, buf, sizeof(buf));
-    if (nread <= 0) {
-        LOG_ERROR("Connection closed");
-        return -1;
-    }
-    pktbuf_insert(gdbserver, buf, nread);
-    return nread;
-}
-
 void
 write_data_raw(WASMGDBServer *gdbserver, const uint8 *data, ssize_t len)
 {
@@ -139,40 +89,3 @@ write_binary_packet(WASMGDBServer *gdbserver, const char *pfx,
     write_packet_bytes(gdbserver, buf, buf_num_bytes);
     wasm_runtime_free(buf);
 }
-
-bool
-skip_to_packet_start(WASMGDBServer *gdbserver)
-{
-    ssize_t start_index = -1, i;
-
-    for (i = 0; i < (ssize_t)gdbserver->pkt.size; ++i) {
-        if (gdbserver->pkt.buf[i] == '$') {
-            start_index = i;
-            break;
-        }
-    }
-
-    if (start_index < 0) {
-        pktbuf_clear(gdbserver);
-        return false;
-    }
-
-    pktbuf_erase_head(gdbserver, start_index);
-
-    bh_assert(1 <= gdbserver->pkt.size);
-    bh_assert('$' == gdbserver->pkt.buf[0]);
-
-    return true;
-}
-
-bool
-read_packet(WASMGDBServer *gdbserver)
-{
-    while (!skip_to_packet_start(gdbserver)) {
-        if (read_data_once(gdbserver) < 0)
-            return false;
-    }
-    if (!gdbserver->noack)
-        write_data_raw(gdbserver, (uint8 *)"+", 1);
-    return true;
-}

+ 2 - 5
core/iwasm/libraries/debug-engine/packets.h

@@ -8,13 +8,10 @@
 
 #include "gdbserver.h"
 
-bool
-read_packet(WASMGDBServer *gdbserver);
-
 void
-write_packet(WASMGDBServer *gdbserver, const char *data);
+write_data_raw(WASMGDBServer *gdbserver, const uint8 *data, ssize_t len);
 
 void
-inbuf_erase_head(WASMGDBServer *gdbserver, ssize_t end);
+write_packet(WASMGDBServer *gdbserver, const char *data);
 
 #endif

+ 20 - 4
core/iwasm/libraries/thread-mgr/thread_manager.c

@@ -284,6 +284,22 @@ wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env)
 {
     bool ret = true;
     bh_assert(exec_env->cluster == cluster);
+
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    /* Wait for debugger control thread to process the
+       stop event of this thread */
+    if (cluster->debug_inst) {
+        /* lock the debug_inst->wait_lock so
+           other threads can't fire stop events */
+        os_mutex_lock(&cluster->debug_inst->wait_lock);
+        while (cluster->debug_inst->stopped_thread == exec_env) {
+            os_cond_wait(&cluster->debug_inst->wait_cond,
+                         &cluster->debug_inst->wait_lock);
+        }
+        os_mutex_unlock(&cluster->debug_inst->wait_lock);
+    }
+#endif
+
     os_mutex_lock(&cluster->lock);
     if (bh_list_remove(&cluster->exec_env_list, exec_env) != 0)
         ret = false;
@@ -447,6 +463,9 @@ thread_manager_start_routine(void *arg)
     free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom);
     /* Detach the native thread here to ensure the resources are freed */
     wasm_cluster_detach_thread(exec_env);
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    wasm_cluster_thread_exited(exec_env);
+#endif
     /* Remove and destroy exec_env */
     wasm_cluster_del_exec_env(cluster, exec_env);
     wasm_exec_env_destroy_internal(exec_env);
@@ -563,9 +582,7 @@ notify_debug_instance(WASMExecEnv *exec_env)
         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);
+    on_thread_stop_event(cluster->debug_inst, exec_env);
 }
 
 void
@@ -744,7 +761,6 @@ wasm_cluster_cancel_thread(WASMExecEnv *exec_env)
     /* Set the termination flag */
 #if WASM_ENABLE_DEBUG_INTERP != 0
     wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM);
-    wasm_cluster_thread_exited(exec_env);
 #else
     exec_env->suspend_flags.flags |= 0x01;
 #endif

+ 16 - 0
core/shared/platform/common/posix/posix_socket.c

@@ -71,6 +71,22 @@ fail:
     return BHT_ERROR;
 }
 
+int
+os_socket_settimeout(bh_socket_t socket, uint64 timeout_us)
+{
+    struct timeval tv;
+    tv.tv_sec = timeout_us / 1000000UL;
+    tv.tv_usec = timeout_us % 1000000UL;
+
+    if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv,
+                   sizeof(tv))
+        != 0) {
+        return BHT_ERROR;
+    }
+
+    return BHT_OK;
+}
+
 int
 os_socket_listen(bh_socket_t socket, int max_client)
 {

+ 11 - 0
core/shared/platform/include/platform_api_extension.h

@@ -226,6 +226,17 @@ os_socket_create(bh_socket_t *sock, int tcp_or_udp);
 int
 os_socket_bind(bh_socket_t socket, const char *addr, int *port);
 
+/**
+ * Set timeout for the given socket
+ *
+ * @param socket the socket to set timeout
+ * @param timeout_us timeout in microseconds
+ *
+ * @return 0 if success, -1 otherwise
+ */
+int
+os_socket_settimeout(bh_socket_t socket, uint64 timeout_us);
+
 /**
  * Make the socket as a passive socket to accept incoming connection requests
  *

+ 14 - 0
core/shared/platform/windows/win_socket.c

@@ -85,6 +85,20 @@ fail:
     return BHT_ERROR;
 }
 
+int
+os_socket_settimeout(bh_socket_t socket, uint64 timeout_us)
+{
+    DWORD tv = (DWORD)(timeout_us / 1000UL);
+
+    if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv,
+                   sizeof(tv))
+        != 0) {
+        return BHT_ERROR;
+    }
+
+    return BHT_OK;
+}
+
 int
 os_socket_listen(bh_socket_t socket, int max_client)
 {