Rémi Bernon : server: Implement DBG_REPLY_LATER handling.

Alexandre Julliard julliard at winehq.org
Fri Mar 27 16:14:38 CDT 2020


Module: wine
Branch: master
Commit: e2a1f00a3850dbce89ec0f85bc46fcd858f9a798
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=e2a1f00a3850dbce89ec0f85bc46fcd858f9a798

Author: Rémi Bernon <rbernon at codeweavers.com>
Date:   Tue Mar 24 16:05:50 2020 +0100

server: Implement DBG_REPLY_LATER handling.

This flag causes the debug event to be replayed after the target thread
continues. It can be used, after suspending the thread, to resume other
threads and later return to the breaking.

This will help implementing gdb continue/step packets correctly.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/kernel32/tests/debugger.c |  4 +--
 server/debugger.c              | 73 ++++++++++++++++++++++++++++++++++++++++--
 server/object.h                |  1 +
 server/thread.c                |  3 +-
 4 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c
index 8062c29461..33e18d82e6 100644
--- a/dlls/kernel32/tests/debugger.c
+++ b/dlls/kernel32/tests/debugger.c
@@ -1278,7 +1278,7 @@ static void test_debugger(const char *argv0)
     ok(ctx.ev.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT, "dwDebugEventCode = %d\n", ctx.ev.dwDebugEventCode);
 
     if ((skip_reply_later = !ContinueDebugEvent(ctx.ev.dwProcessId, ctx.ev.dwThreadId, DBG_REPLY_LATER)))
-        todo_wine win_skip("Skipping unsupported DBG_REPLY_LATER tests\n");
+        win_skip("Skipping unsupported DBG_REPLY_LATER tests\n");
     else
     {
         DEBUG_EVENT de;
@@ -1402,7 +1402,7 @@ static void test_debugger(const char *argv0)
     else win_skip("call_debug_service_code not supported on this architecture\n");
 
     if (skip_reply_later)
-        todo_wine win_skip("Skipping unsupported DBG_REPLY_LATER tests\n");
+        win_skip("Skipping unsupported DBG_REPLY_LATER tests\n");
     else if (sizeof(loop_code) > 1)
     {
         HANDLE thread_a, thread_b;
diff --git a/server/debugger.c b/server/debugger.c
index 5b06b73b4b..5ff63b92b9 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -38,7 +38,7 @@
 #include "thread.h"
 #include "request.h"
 
-enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_CONTINUED };
+enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_DELAYED, EVENT_CONTINUED };
 
 /* debug event */
 struct debug_event
@@ -265,6 +265,31 @@ static void link_event( struct debug_event *event )
     }
 }
 
+/* resume a delayed debug event already in the queue */
+static void resume_event( struct debug_event *event )
+{
+    struct debug_ctx *debug_ctx = event->debugger->debug_ctx;
+
+    assert( debug_ctx );
+    event->state = EVENT_QUEUED;
+    if (!event->sender->process->debug_event)
+    {
+        grab_object( debug_ctx );
+        wake_up( &debug_ctx->obj, 0 );
+        release_object( debug_ctx );
+    }
+}
+
+/* delay a debug event already in the queue to be replayed when thread wakes up */
+static void delay_event( struct debug_event *event )
+{
+    struct debug_ctx *debug_ctx = event->debugger->debug_ctx;
+
+    assert( debug_ctx );
+    event->state = EVENT_DELAYED;
+    if (event->sender->process->debug_event == event) event->sender->process->debug_event = NULL;
+}
+
 /* find the next event that we can send to the debugger */
 static struct debug_event *find_event_to_send( struct debug_ctx *debug_ctx )
 {
@@ -273,6 +298,7 @@ static struct debug_event *find_event_to_send( struct debug_ctx *debug_ctx )
     LIST_FOR_EACH_ENTRY( event, &debug_ctx->event_queue, struct debug_event, entry )
     {
         if (event->state == EVENT_SENT) continue;  /* already sent */
+        if (event->state == EVENT_DELAYED) continue;  /* delayed until thread resumes */
         if (event->sender->process->debug_event) continue;  /* process busy with another one */
         return event;
     }
@@ -365,6 +391,29 @@ static int continue_debug_event( struct process *process, struct thread *thread,
     {
         struct debug_event *event;
 
+        if (status == DBG_REPLY_LATER)
+        {
+            /* if thread is suspended, delay all its events and resume process
+             * if not, reset the event for immediate replay */
+            LIST_FOR_EACH_ENTRY( event, &debug_ctx->event_queue, struct debug_event, entry )
+            {
+                if (event->sender != thread) continue;
+                if (thread->suspend)
+                {
+                    delay_event( event );
+                    resume_process( process );
+                }
+                else if (event->state == EVENT_SENT)
+                {
+                    assert( event->sender->process->debug_event == event );
+                    event->sender->process->debug_event = NULL;
+                    resume_event( event );
+                    return 1;
+                }
+            }
+            return 1;
+        }
+
         /* find the event in the queue */
         LIST_FOR_EACH_ENTRY( event, &debug_ctx->event_queue, struct debug_event, entry )
         {
@@ -372,7 +421,6 @@ static int continue_debug_event( struct process *process, struct thread *thread,
             if (event->sender == thread)
             {
                 assert( event->sender->process->debug_event == event );
-
                 event->status = status;
                 event->state  = EVENT_CONTINUED;
                 wake_up( &event->obj, 0 );
@@ -430,6 +478,24 @@ void generate_debug_event( struct thread *thread, int code, const void *arg )
     }
 }
 
+void resume_delayed_debug_events( struct thread *thread )
+{
+    struct thread *debugger = thread->process->debugger;
+    struct debug_event *event;
+
+    if (debugger)
+    {
+        assert( debugger->debug_ctx );
+        LIST_FOR_EACH_ENTRY( event, &debugger->debug_ctx->event_queue, struct debug_event, entry )
+        {
+            if (event->sender != thread) continue;
+            if (event->state != EVENT_DELAYED) continue;
+            resume_event( event );
+            suspend_process( thread->process );
+        }
+    }
+}
+
 /* attach a process to a debugger thread and suspend it */
 static int debugger_attach( struct process *process, struct thread *debugger )
 {
@@ -615,7 +681,8 @@ DECL_HANDLER(continue_debug_event)
     struct process *process;
 
     if (req->status != DBG_EXCEPTION_NOT_HANDLED &&
-        req->status != DBG_CONTINUE)
+        req->status != DBG_CONTINUE &&
+        req->status != DBG_REPLY_LATER)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
diff --git a/server/object.h b/server/object.h
index 8114a8fb6b..3144eb5667 100644
--- a/server/object.h
+++ b/server/object.h
@@ -207,6 +207,7 @@ extern void sock_init(void);
 
 extern int set_process_debugger( struct process *process, struct thread *debugger );
 extern void generate_debug_event( struct thread *thread, int code, const void *arg );
+extern void resume_delayed_debug_events( struct thread *thread );
 extern void generate_startup_debug_events( struct process *process, client_ptr_t entry );
 extern void debug_exit_thread( struct thread *thread );
 
diff --git a/server/thread.c b/server/thread.c
index edf70c61bd..6e677c8cf2 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -612,7 +612,8 @@ int resume_thread( struct thread *thread )
     int old_count = thread->suspend;
     if (thread->suspend > 0)
     {
-        if (!(--thread->suspend + thread->process->suspend)) wake_thread( thread );
+        if (!(--thread->suspend)) resume_delayed_debug_events( thread );
+        if (!(thread->suspend + thread->process->suspend)) wake_thread( thread );
     }
     return old_count;
 }




More information about the wine-cvs mailing list