Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/kernelbase/debug.c | 7 +++++++ dlls/ntdll/tests/exception.c | 1 + 2 files changed, 8 insertions(+)
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 53b95aa6dda..d99309377f0 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -70,6 +70,13 @@ BOOL WINAPI DECLSPEC_HOTPATCH CheckRemoteDebuggerPresent( HANDLE process, BOOL * BOOL WINAPI DECLSPEC_HOTPATCH ContinueDebugEvent( DWORD pid, DWORD tid, DWORD status ) { BOOL ret; + + if (status != DBG_EXCEPTION_NOT_HANDLED && status != DBG_CONTINUE) + { + FIXME("Unknown status %x\n", status); + return FALSE; + } + SERVER_START_REQ( continue_debug_event ) { req->pid = pid; diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index e8b481e703f..9cb9fc50d5d 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -999,6 +999,7 @@ static void test_debugger(void) { continuestatus = DBG_CONTINUE; ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); + ok(!ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef), "ContinueDebugEvent unexpectedly succeeded\n");
if (de.dwThreadId != pi.dwThreadId) {
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/exception.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 9cb9fc50d5d..1e14f47e04e 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1001,6 +1001,21 @@ static void test_debugger(void) ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); ok(!ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef), "ContinueDebugEvent unexpectedly succeeded\n");
+ todo_wine + ok((ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_REPLY_LATER)) || broken(!ret) /* only on w10 > 1507 */, + "ContinueDebugEvent DBG_REPLY_LATER failed\n"); + + if (!ret) + skip("Skipping unsupported DBG_REPLY_LATER tests\n"); + else + { + DEBUG_EVENT delayed_de; + ok(WaitForDebugEvent(&delayed_de, 50), "reading delayed debug event\n"); + ok(de.dwDebugEventCode == delayed_de.dwDebugEventCode, "delayed event differ, code:%x was:%x\n", delayed_de.dwDebugEventCode, de.dwDebugEventCode); + ok(de.dwProcessId == delayed_de.dwProcessId, "delayed event differ, pid:%x was:%x\n", delayed_de.dwProcessId, de.dwProcessId); + ok(de.dwThreadId == delayed_de.dwThreadId, "delayed event differ, tid:%x was:%x\n", delayed_de.dwThreadId, de.dwThreadId); + } + if (de.dwThreadId != pi.dwThreadId) { trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
On 31.01.2020 16:32, Rémi Bernon wrote:
todo_wine
ok((ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_REPLY_LATER)) || broken(!ret) /* only on w10 > 1507 */,
"ContinueDebugEvent DBG_REPLY_LATER failed\n");
if (!ret)
skip("Skipping unsupported DBG_REPLY_LATER tests\n");
It's not really important so feel free to ignore, but win_skip could be slightly cleaner than broken(). I would write it as:
if (ContinueDebugEvent(...))
{
}
else todo_wine win_skip(....);
else
{
DEBUG_EVENT delayed_de;
ok(WaitForDebugEvent(&delayed_de, 50), "reading delayed debug event\n");
ok(de.dwDebugEventCode == delayed_de.dwDebugEventCode, "delayed event differ, code:%x was:%x\n", delayed_de.dwDebugEventCode, de.dwDebugEventCode);
ok(de.dwProcessId == delayed_de.dwProcessId, "delayed event differ, pid:%x was:%x\n", delayed_de.dwProcessId, de.dwProcessId);
ok(de.dwThreadId == delayed_de.dwThreadId, "delayed event differ, tid:%x was:%x\n", delayed_de.dwThreadId, de.dwThreadId);
}
It would be interesting to do a bit more here. I would suggest to suspend thread, poll for events and make sure it's not reported. Then resume the thread and check that it's reported.
Thanks,
Jacek
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@codeweavers.com --- dlls/kernelbase/debug.c | 2 +- dlls/ntdll/tests/exception.c | 1 - server/debugger.c | 58 +++++++++++++++++++++++++++++++++++- server/object.h | 1 + server/thread.c | 4 ++- 5 files changed, 62 insertions(+), 4 deletions(-)
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index d99309377f0..422fb029242 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -71,7 +71,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH ContinueDebugEvent( DWORD pid, DWORD tid, DWORD st { BOOL ret;
- if (status != DBG_EXCEPTION_NOT_HANDLED && status != DBG_CONTINUE) + if (status != DBG_EXCEPTION_NOT_HANDLED && status != DBG_CONTINUE && status != DBG_REPLY_LATER) { FIXME("Unknown status %x\n", status); return FALSE; diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 1e14f47e04e..1bd275d7074 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1001,7 +1001,6 @@ static void test_debugger(void) ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n"); ok(!ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef), "ContinueDebugEvent unexpectedly succeeded\n");
- todo_wine ok((ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_REPLY_LATER)) || broken(!ret) /* only on w10 > 1507 */, "ContinueDebugEvent DBG_REPLY_LATER failed\n");
diff --git a/server/debugger.c b/server/debugger.c index a5c39aa33e7..24038ab1105 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; } @@ -373,6 +399,15 @@ static int continue_debug_event( struct process *process, struct thread *thread, { assert( event->sender->process->debug_event == event );
+ if (status == DBG_REPLY_LATER) + { + delay_event( event ); + /* the target thread is not suspended, replay the event immediately */ + if (!event->sender->suspend) resume_event( event ); + else resume_process( process ); + return 1; + } + event->status = status; event->state = EVENT_CONTINUED; wake_up( &event->obj, 0 ); @@ -430,6 +465,27 @@ void generate_debug_event( struct thread *thread, int code, const void *arg ) } }
+int 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->state != EVENT_DELAYED) continue; + if (event->sender != thread) continue; + resume_event( event ); + suspend_process( thread->process ); + return 1; + } + } + + return 0; +} + /* attach a process to a debugger thread and suspend it */ static int debugger_attach( struct process *process, struct thread *debugger ) { diff --git a/server/object.h b/server/object.h index 4a486e0d633..84d2cb227e4 100644 --- a/server/object.h +++ b/server/object.h @@ -206,6 +206,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 int 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 80db41b48d2..aec4c1acf4e 100644 --- a/server/thread.c +++ b/server/thread.c @@ -612,7 +612,9 @@ 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 + thread->process->suspend) && + !resume_delayed_debug_events( thread )) + wake_thread( thread ); } return old_count; }
Hi Rémi,
On 31.01.2020 16:32, Rémi Bernon wrote:
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 53b95aa6dda..d99309377f0 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -70,6 +70,13 @@ BOOL WINAPI DECLSPEC_HOTPATCH CheckRemoteDebuggerPresent( HANDLE process, BOOL * BOOL WINAPI DECLSPEC_HOTPATCH ContinueDebugEvent( DWORD pid, DWORD tid, DWORD status ) { BOOL ret;
- if (status != DBG_EXCEPTION_NOT_HANDLED && status != DBG_CONTINUE)
- {
FIXME("Unknown status %x\n", status);
return FALSE;
- }
It's not a big deal because it's FIXME anyway, but since you're at this and add test anyway, we should probably set last error in this case. I would also consider validating that on server side.
Thanks,
Jacek