Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/exception.c | 4 ++++ server/debugger.c | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index e8b481e703f..07e05e45c90 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1000,6 +1000,10 @@ static void test_debugger(void) continuestatus = DBG_CONTINUE; ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
+ ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef); + ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError()); + if (de.dwThreadId != pi.dwThreadId) { trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode); diff --git a/server/debugger.c b/server/debugger.c index a5c39aa33e7..5b06b73b4be 100644 --- a/server/debugger.c +++ b/server/debugger.c @@ -612,8 +612,16 @@ DECL_HANDLER(wait_debug_event) /* Continue a debug event */ DECL_HANDLER(continue_debug_event) { - struct process *process = get_process_from_id( req->pid ); - if (process) + struct process *process; + + if (req->status != DBG_EXCEPTION_NOT_HANDLED && + req->status != DBG_CONTINUE) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + if ((process = get_process_from_id( req->pid ))) { struct thread *thread = get_thread_from_id( req->tid ); if (thread)
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/ntdll/tests/exception.c | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 07e05e45c90..71bca51a31f 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -974,7 +974,8 @@ static void test_debugger(void) char cmdline[MAX_PATH]; PROCESS_INFORMATION pi; STARTUPINFOA si = { 0 }; - DEBUG_EVENT de; + DEBUG_EVENT de, de0, de1; + HANDLE thread; DWORD continuestatus; PVOID code_mem_address = NULL; NTSTATUS status; @@ -1004,6 +1005,42 @@ static void test_debugger(void) ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n"); ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
+ if (!ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_REPLY_LATER)) + todo_wine win_skip("Skipping unsupported DBG_REPLY_LATER tests\n"); + else + { + ok(WaitForDebugEvent(&de0, 50), "WaitForDebugEvent failed, last error:%u\n", GetLastError()); + ok(de.dwDebugEventCode == de0.dwDebugEventCode, "delayed event differ, code:%x was:%x\n", de0.dwDebugEventCode, de.dwDebugEventCode); + ok(de.dwProcessId == de0.dwProcessId, "delayed event differ, pid:%x was:%x\n", de0.dwProcessId, de.dwProcessId); + ok(de.dwThreadId == de0.dwThreadId, "delayed event differ, tid:%x was:%x\n", de0.dwThreadId, de.dwThreadId); + + if (de.dwDebugEventCode != CREATE_PROCESS_DEBUG_EVENT && + de.dwDebugEventCode != CREATE_THREAD_DEBUG_EVENT && + de.dwDebugEventCode != EXIT_THREAD_DEBUG_EVENT && + de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) + { + thread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, de.dwThreadId); + ok(thread != INVALID_HANDLE_VALUE, "OpenThread failed, last error:%u\n", GetLastError()); + + status = NtSuspendThread(thread, NULL); + ok(!status, "NtSuspendThread failed, last error:%u\n", GetLastError()); + + ok(ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_REPLY_LATER), + "ContinueDebugEvent failed, last error:%u\n", GetLastError()); + + ok(!WaitForDebugEvent(&de1, 50), "WaitForDebugEvent succeeded.\n"); + status = NtResumeThread(thread, NULL); + + ok(!status, "NtResumeThread failed, last error:%u\n", GetLastError()); + CloseHandle(thread); + + ok(WaitForDebugEvent(&de1, 50), "WaitForDebugEvent failed, last error:%u\n", GetLastError()); + ok(de.dwDebugEventCode == de1.dwDebugEventCode, "delayed event differ, code:%x was:%x\n", de1.dwDebugEventCode, de.dwDebugEventCode); + ok(de.dwProcessId == de1.dwProcessId, "delayed event differ, pid:%x was:%x\n", de1.dwProcessId, de.dwProcessId); + ok(de.dwThreadId == de1.dwThreadId, "delayed event differ, tid:%x was:%x\n", de1.dwThreadId, de.dwThreadId); + } + } + if (de.dwThreadId != pi.dwThreadId) { trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
Hi Rémi,
On 10.02.2020 17:48, Rémi Bernon wrote:
Signed-off-by: Rémi Bernonrbernon@codeweavers.com
dlls/ntdll/tests/exception.c | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)
The test itself looks good to me. However, looking at context and other stuff in test_debugger(), it feels a bit misplaced. How about adding that to kernel32 test_debugger() instead?
Thanks,
Jacek
On 2/13/20 8:45 PM, Jacek Caban wrote:
Hi Rémi,
On 10.02.2020 17:48, Rémi Bernon wrote:
Signed-off-by: Rémi Bernonrbernon@codeweavers.com
dlls/ntdll/tests/exception.c | 39 +++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-)
The test itself looks good to me. However, looking at context and other stuff in test_debugger(), it feels a bit misplaced. How about adding that to kernel32 test_debugger() instead?
Thanks,
Jacek
I hesitated between ntdll and kernel32 and it felt easier to put it in the loop here. I don't mind moving it though.
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/ntdll/tests/exception.c | 2 +- server/debugger.c | 73 ++++++++++++++++++++++++++++++++++-- server/object.h | 1 + server/thread.c | 3 +- 4 files changed, 74 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 71bca51a31f..66d301bd4e6 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1006,7 +1006,7 @@ static void test_debugger(void) ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
if (!ContinueDebugEvent(de.dwProcessId, de.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 { ok(WaitForDebugEvent(&de0, 50), "WaitForDebugEvent failed, last error:%u\n", GetLastError()); diff --git a/server/debugger.c b/server/debugger.c index 5b06b73b4be..5ff63b92b9c 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 4a486e0d633..7f664ad2821 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 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 80db41b48d2..006fbb85aa0 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; }