فهرست منبع

Allow source debugger reconnection (#1667)

Allow to wait for a new debugger connection once the previous one
is disconnected:
- when receiving a detach command
- when the client socket is closed (for example, lldb process is killed)
TianlongLiang 3 سال پیش
والد
کامیت
3b4033aceb

+ 112 - 53
core/iwasm/libraries/debug-engine/debug_engine.c

@@ -55,9 +55,21 @@ allocate_instance_id()
 }
 
 static bool
-should_stop(WASMDebugControlThread *control_thread)
+is_thread_running(WASMDebugControlThread *control_thread)
 {
-    return control_thread->status != RUNNING;
+    return control_thread->status == RUNNING;
+}
+
+static bool
+is_thread_stopped(WASMDebugControlThread *control_thread)
+{
+    return control_thread->status == STOPPED;
+}
+
+static bool
+is_thread_detached(WASMDebugControlThread *control_thread)
+{
+    return control_thread->status == DETACHED;
 }
 
 static void *
@@ -109,73 +121,88 @@ control_thread_routine(void *arg)
     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);
+        LOG_ERROR("Failed while listening for debugger\n");
         return NULL;
     }
 
+    /* outer infinite loop: try to connect with the debugger */
     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;
+        /* wait lldb client to connect */
+        if (!wasm_gdbserver_accept(control_thread->server)) {
+            LOG_ERROR("Failed while accepting debugger connection\n");
+            return NULL;
+        }
+
+        control_thread->status = RUNNING;
+
+        /* inner infinite loop: keep serving until detach */
+        while (true) {
+            os_mutex_lock(&control_thread->wait_lock);
+            if (is_thread_running(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);
+                    wasm_debug_instance_set_cur_thread(
+                        debug_inst, debug_inst->stopped_thread->handle);
 
-                send_thread_stop_status(control_thread->server, status, tid);
+                    send_thread_stop_status(control_thread->server, status,
+                                            tid);
 
-                debug_inst->current_state = APP_STOPPED;
-                debug_inst->stopped_thread = NULL;
+                    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);
+                    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;
+                /* Processing incoming requests */
+                if (!wasm_gdbserver_handle_packet(control_thread->server)) {
+                    control_thread->status = STOPPED;
+                    LOG_VERBOSE("control thread of debug object [%p] stopped\n",
+                                debug_inst);
+                    wasm_close_gdbserver(control_thread->server);
+                }
+            }
+            else if (is_thread_detached(control_thread)) {
+                os_mutex_unlock(&control_thread->wait_lock);
+                break;
+            }
+            else if (is_thread_stopped(control_thread)) {
+                os_mutex_unlock(&control_thread->wait_lock);
+                return NULL;
             }
-        }
-        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 *
@@ -987,6 +1014,38 @@ wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance)
     return true;
 }
 
+bool
+wasm_debug_instance_detach(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;
+
+    wasm_gdbserver_detach(instance->control_thread->server);
+
+    while (exec_env) {
+        if (instance->current_state == APP_STOPPED) {
+            /* Resume all threads since remote debugger detached*/
+            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);
+    }
+
+    /* relaunch, accept new debug connection */
+    instance->current_state = DBG_LAUNCHING;
+    instance->control_thread->status = DETACHED;
+
+    return true;
+}
+
 bool
 wasm_debug_instance_kill(WASMDebugInstance *instance)
 {

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

@@ -12,6 +12,7 @@
 
 typedef enum WASMDebugControlThreadStatus {
     RUNNING,
+    DETACHED,
     STOPPED,
 } WASMDebugControlThreadStatus;
 
@@ -185,6 +186,9 @@ wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance);
 bool
 wasm_debug_instance_continue(WASMDebugInstance *instance);
 
+bool
+wasm_debug_instance_detach(WASMDebugInstance *instance);
+
 bool
 wasm_debug_instance_kill(WASMDebugInstance *instance);
 

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

@@ -34,6 +34,7 @@ static const struct packet_handler_elem packet_handler_table[255] = {
     DEL_HANDLER('c', handle_continue_request),
     DEL_HANDLER('k', handle_kill_request),
     DEL_HANDLER('_', handle____request),
+    DEL_HANDLER('D', handle_detach_request),
 };
 
 WASMGDBServer *
@@ -89,7 +90,6 @@ fail:
 bool
 wasm_gdbserver_listen(WASMGDBServer *server)
 {
-    bh_socket_t sockt_fd = (bh_socket_t)-1;
     int32 ret;
 
     ret = os_socket_listen(server->listen_fd, 1);
@@ -98,6 +98,23 @@ wasm_gdbserver_listen(WASMGDBServer *server)
         goto fail;
     }
 
+    LOG_VERBOSE("listen for gdb client");
+    return true;
+
+fail:
+    os_socket_shutdown(server->listen_fd);
+    os_socket_close(server->listen_fd);
+    return false;
+}
+
+bool
+wasm_gdbserver_accept(WASMGDBServer *server)
+{
+
+    bh_socket_t sockt_fd = (bh_socket_t)-1;
+
+    LOG_VERBOSE("waiting for gdb client to connect...");
+
     os_socket_accept(server->listen_fd, &sockt_fd, NULL, NULL);
     if (sockt_fd < 0) {
         LOG_ERROR("wasm gdb server error: socket accept failed");
@@ -115,6 +132,15 @@ fail:
     return false;
 }
 
+void
+wasm_gdbserver_detach(WASMGDBServer *server)
+{
+    if (server->socket_fd > 0) {
+        os_socket_shutdown(server->socket_fd);
+        os_socket_close(server->socket_fd);
+    }
+}
+
 void
 wasm_close_gdbserver(WASMGDBServer *server)
 {
@@ -263,8 +289,9 @@ wasm_gdbserver_handle_packet(WASMGDBServer *server)
     n = os_socket_recv(server->socket_fd, buf, sizeof(buf));
 
     if (n == 0) {
-        LOG_VERBOSE("Debugger disconnected");
-        return false;
+        handle_detach_request(server, NULL);
+        LOG_VERBOSE("Debugger disconnected, waiting for debugger reconnection");
+        return true;
     }
     else if (n < 0) {
 #if defined(BH_PLATFORM_WINDOWS)

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

@@ -56,6 +56,12 @@ wasm_create_gdbserver(const char *host, int32 *port);
 bool
 wasm_gdbserver_listen(WASMGDBServer *server);
 
+bool
+wasm_gdbserver_accept(WASMGDBServer *server);
+
+void
+wasm_gdbserver_detach(WASMGDBServer *server);
+
 void
 wasm_close_gdbserver(WASMGDBServer *server);
 

+ 10 - 0
core/iwasm/libraries/debug-engine/handler.c

@@ -777,3 +777,13 @@ handle____request(WASMGDBServer *server, char *payload)
         handle_free(server, args);
     }
 }
+
+void
+handle_detach_request(WASMGDBServer *server, char *payload)
+{
+    if (payload != NULL) {
+        write_packet(server, "OK");
+    }
+    wasm_debug_instance_detach(
+        (WASMDebugInstance *)server->thread->debug_instance);
+}

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

@@ -62,6 +62,9 @@ handle_kill_request(WASMGDBServer *server, char *payload);
 void
 handle____request(WASMGDBServer *server, char *payload);
 
+void
+handle_detach_request(WASMGDBServer *server, char *payload);
+
 void
 send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid);
 #endif