From 7999af82448c340b28e3d6e412463c5cdcc6cea6 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Mon, 1 Feb 2021 11:45:29 +0100
Subject: [PATCH] ntdll: Implement NtWaitForDebugEvent().

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
---
 dlls/kernel32/tests/loader.c   |   1 -
 dlls/kernelbase/debug.c        | 107 +--------------------------------
 dlls/kernelbase/sync.c         |  29 +++++++++
 dlls/ntdll/ntdll.spec          |   6 +-
 dlls/ntdll/process.c           |   8 +++
 dlls/ntdll/unix/sync.c         | 102 +++++++++++++++++++++++++++++++
 include/wine/server_protocol.h |   9 +--
 include/winternl.h             |   2 +
 server/debugger.c              |  68 +++++++++------------
 server/process.c               |   8 +--
 server/protocol.def            |  20 +++---
 server/request.h               |   5 +-
 server/thread.c                |   2 +-
 server/trace.c                 |  61 ++++++++++---------
 14 files changed, 220 insertions(+), 208 deletions(-)

diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c
index 47fb3cf4649..861fc321729 100644
--- a/dlls/kernel32/tests/loader.c
+++ b/dlls/kernel32/tests/loader.c
@@ -2614,7 +2614,6 @@ todo_wine
         SetLastError(0xdeadbeef);
         ret = WaitForDebugEvent(&de, 0);
         ok(!ret, "WaitForDebugEvent should fail\n");
-todo_wine
         ok(GetLastError() == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
 
         SetLastError(0xdeadbeef);
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c
index 272f255cf0c..8f8389ac372 100644
--- a/dlls/kernelbase/debug.c
+++ b/dlls/kernelbase/debug.c
@@ -20,6 +20,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -33,7 +34,6 @@
 #include "werapi.h"
 
 #include "wine/exception.h"
-#include "wine/server.h"
 #include "wine/asm.h"
 #include "kernelbase.h"
 #include "wine/debug.h"
@@ -313,111 +313,6 @@ LPTOP_LEVEL_EXCEPTION_FILTER WINAPI DECLSPEC_HOTPATCH SetUnhandledExceptionFilte
 }
 
 
-/******************************************************************************
- *           WaitForDebugEvent   (kernelbase.@)
- */
-BOOL WINAPI DECLSPEC_HOTPATCH WaitForDebugEvent( DEBUG_EVENT *event, DWORD timeout )
-{
-    BOOL ret;
-    DWORD res;
-    int i;
-
-    for (;;)
-    {
-        HANDLE wait = 0;
-        debug_event_t data;
-        SERVER_START_REQ( wait_debug_event )
-        {
-            req->get_handle = (timeout != 0);
-            wine_server_set_reply( req, &data, sizeof(data) );
-            if (!(ret = !wine_server_call_err( req ))) goto done;
-
-            if (!wine_server_reply_size( reply ))  /* timeout */
-            {
-                wait = wine_server_ptr_handle( reply->wait );
-                ret = FALSE;
-                goto done;
-            }
-            event->dwDebugEventCode = data.code;
-            event->dwProcessId      = (DWORD)reply->pid;
-            event->dwThreadId       = (DWORD)reply->tid;
-            switch (data.code)
-            {
-            case EXCEPTION_DEBUG_EVENT:
-                if (data.exception.exc_code == DBG_PRINTEXCEPTION_C && data.exception.nb_params >= 2)
-                {
-                    event->dwDebugEventCode = OUTPUT_DEBUG_STRING_EVENT;
-                    event->u.DebugString.lpDebugStringData  = wine_server_get_ptr( data.exception.params[1] );
-                    event->u.DebugString.fUnicode           = FALSE;
-                    event->u.DebugString.nDebugStringLength = data.exception.params[0];
-                    break;
-                }
-                else if (data.exception.exc_code == DBG_RIPEXCEPTION && data.exception.nb_params >= 2)
-                {
-                    event->dwDebugEventCode = RIP_EVENT;
-                    event->u.RipInfo.dwError = data.exception.params[0];
-                    event->u.RipInfo.dwType  = data.exception.params[1];
-                    break;
-                }
-                event->u.Exception.dwFirstChance = data.exception.first;
-                event->u.Exception.ExceptionRecord.ExceptionCode    = data.exception.exc_code;
-                event->u.Exception.ExceptionRecord.ExceptionFlags   = data.exception.flags;
-                event->u.Exception.ExceptionRecord.ExceptionRecord  = wine_server_get_ptr( data.exception.record );
-                event->u.Exception.ExceptionRecord.ExceptionAddress = wine_server_get_ptr( data.exception.address );
-                event->u.Exception.ExceptionRecord.NumberParameters = data.exception.nb_params;
-                for (i = 0; i < data.exception.nb_params; i++)
-                    event->u.Exception.ExceptionRecord.ExceptionInformation[i] = data.exception.params[i];
-                break;
-            case CREATE_THREAD_DEBUG_EVENT:
-                event->u.CreateThread.hThread           = wine_server_ptr_handle( data.create_thread.handle );
-                event->u.CreateThread.lpThreadLocalBase = wine_server_get_ptr( data.create_thread.teb );
-                event->u.CreateThread.lpStartAddress    = wine_server_get_ptr( data.create_thread.start );
-                break;
-            case CREATE_PROCESS_DEBUG_EVENT:
-                event->u.CreateProcessInfo.hFile                 = wine_server_ptr_handle( data.create_process.file );
-                event->u.CreateProcessInfo.hProcess              = wine_server_ptr_handle( data.create_process.process );
-                event->u.CreateProcessInfo.hThread               = wine_server_ptr_handle( data.create_process.thread );
-                event->u.CreateProcessInfo.lpBaseOfImage         = wine_server_get_ptr( data.create_process.base );
-                event->u.CreateProcessInfo.dwDebugInfoFileOffset = data.create_process.dbg_offset;
-                event->u.CreateProcessInfo.nDebugInfoSize        = data.create_process.dbg_size;
-                event->u.CreateProcessInfo.lpThreadLocalBase     = wine_server_get_ptr( data.create_process.teb );
-                event->u.CreateProcessInfo.lpStartAddress        = wine_server_get_ptr( data.create_process.start );
-                event->u.CreateProcessInfo.lpImageName           = wine_server_get_ptr( data.create_process.name );
-                event->u.CreateProcessInfo.fUnicode              = data.create_process.unicode;
-                break;
-            case EXIT_THREAD_DEBUG_EVENT:
-                event->u.ExitThread.dwExitCode = data.exit.exit_code;
-                break;
-            case EXIT_PROCESS_DEBUG_EVENT:
-                event->u.ExitProcess.dwExitCode = data.exit.exit_code;
-                break;
-            case LOAD_DLL_DEBUG_EVENT:
-                event->u.LoadDll.hFile                 = wine_server_ptr_handle( data.load_dll.handle );
-                event->u.LoadDll.lpBaseOfDll           = wine_server_get_ptr( data.load_dll.base );
-                event->u.LoadDll.dwDebugInfoFileOffset = data.load_dll.dbg_offset;
-                event->u.LoadDll.nDebugInfoSize        = data.load_dll.dbg_size;
-                event->u.LoadDll.lpImageName           = wine_server_get_ptr( data.load_dll.name );
-                event->u.LoadDll.fUnicode              = data.load_dll.unicode;
-                break;
-            case UNLOAD_DLL_DEBUG_EVENT:
-                event->u.UnloadDll.lpBaseOfDll = wine_server_get_ptr( data.unload_dll.base );
-                break;
-            }
-        done:
-            /* nothing */ ;
-        }
-        SERVER_END_REQ;
-        if (ret) return TRUE;
-        if (!wait) break;
-        res = WaitForSingleObject( wait, timeout );
-        CloseHandle( wait );
-        if (res != STATUS_WAIT_0) break;
-    }
-    SetLastError( ERROR_SEM_TIMEOUT );
-    return FALSE;
-}
-
-
 /*******************************************************************
  *         format_exception_msg
  */
diff --git a/dlls/kernelbase/sync.c b/dlls/kernelbase/sync.c
index e8e51b1c573..f4bee2d134b 100644
--- a/dlls/kernelbase/sync.c
+++ b/dlls/kernelbase/sync.c
@@ -350,6 +350,35 @@ DWORD WINAPI DECLSPEC_HOTPATCH WaitForMultipleObjectsEx( DWORD count, const HAND
 }
 
 
+/******************************************************************************
+ *           WaitForDebugEvent   (kernelbase.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH WaitForDebugEvent( DEBUG_EVENT *event, DWORD timeout )
+{
+    NTSTATUS status;
+    LARGE_INTEGER time;
+    DBGUI_WAIT_STATE_CHANGE state;
+
+    for (;;)
+    {
+        status = DbgUiWaitStateChange( &state, get_nt_timeout( &time, timeout ) );
+        switch (status)
+        {
+        case STATUS_SUCCESS:
+            DbgUiConvertStateChangeStructure( &state, event );
+            return TRUE;
+        case STATUS_USER_APC:
+            continue;
+        case STATUS_TIMEOUT:
+            SetLastError( ERROR_SEM_TIMEOUT );
+            return FALSE;
+        default:
+            return set_ntstatus( status );
+        }
+    }
+}
+
+
 /***********************************************************************
  *           WaitOnAddress   (kernelbase.@)
  */
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index df643445e7d..0a2c2493334 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -43,7 +43,7 @@
 @ stdcall DbgUiRemoteBreakin(ptr)
 @ stdcall DbgUiSetThreadDebugObject(long)
 @ stdcall DbgUiStopDebugging(long)
-@ stub DbgUiWaitStateChange
+@ stdcall DbgUiWaitStateChange(ptr ptr)
 @ stdcall DbgUserBreakPoint()
 @ stdcall EtwEventActivityIdControl(long ptr)
 @ stdcall EtwEventEnabled(int64 ptr)
@@ -422,7 +422,7 @@
 @ stdcall -syscall NtUnmapViewOfSection(long ptr)
 @ stub NtVdmControl
 @ stub NtW32Call
-# @ stub NtWaitForDebugEvent
+@ stdcall -syscall NtWaitForDebugEvent(long long ptr ptr)
 @ stdcall -syscall NtWaitForKeyedEvent(long ptr long ptr)
 @ stdcall -syscall NtWaitForMultipleObjects(long ptr long long ptr)
 @ stub NtWaitForProcessMutant
@@ -1427,7 +1427,7 @@
 @ stdcall -private -syscall ZwUnmapViewOfSection(long ptr) NtUnmapViewOfSection
 @ stub ZwVdmControl
 @ stub ZwW32Call
-# @ stub ZwWaitForDebugEvent
+@ stdcall -private -syscall ZwWaitForDebugEvent(long long ptr ptr) NtWaitForDebugEvent
 @ stdcall -private -syscall ZwWaitForKeyedEvent(long ptr long ptr) NtWaitForKeyedEvent
 @ stdcall -private -syscall ZwWaitForMultipleObjects(long ptr long long ptr) NtWaitForMultipleObjects
 @ stub ZwWaitForProcessMutant
diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c
index 3ff38691bbc..a86eb80e0cc 100644
--- a/dlls/ntdll/process.c
+++ b/dlls/ntdll/process.c
@@ -178,6 +178,14 @@ NTSTATUS WINAPI DbgUiContinue( CLIENT_ID *client, NTSTATUS status )
     return NtDebugContinue( DbgUiGetThreadDebugObject(), client, status );
 }
 
+/***********************************************************************
+ *      DbgUiWaitStateChange (NTDLL.@)
+ */
+NTSTATUS WINAPI DbgUiWaitStateChange( DBGUI_WAIT_STATE_CHANGE *state, LARGE_INTEGER *timeout )
+{
+    return NtWaitForDebugEvent( DbgUiGetThreadDebugObject(), TRUE, timeout, state );
+}
+
 /* helper for DbgUiConvertStateChangeStructure */
 static inline void *get_thread_teb( HANDLE thread )
 {
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index 5132daf9e0e..383e9c93273 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -964,6 +964,108 @@ NTSTATUS WINAPI NtSetInformationDebugObject( HANDLE handle, DEBUGOBJECTINFOCLASS
 }
 
 
+/* convert the server event data to an NT state change; helper for NtWaitForDebugEvent */
+static NTSTATUS event_data_to_state_change( const debug_event_t *data, DBGUI_WAIT_STATE_CHANGE *state )
+{
+    int i;
+
+    switch (data->code)
+    {
+    case DbgIdle:
+    case DbgReplyPending:
+        return STATUS_PENDING;
+    case DbgCreateThreadStateChange:
+    {
+        DBGUI_CREATE_THREAD *info = &state->StateInfo.CreateThread;
+        info->HandleToThread         = wine_server_ptr_handle( data->create_thread.handle );
+        info->NewThread.StartAddress = wine_server_get_ptr( data->create_thread.start );
+        return STATUS_SUCCESS;
+    }
+    case DbgCreateProcessStateChange:
+    {
+        DBGUI_CREATE_PROCESS *info = &state->StateInfo.CreateProcessInfo;
+        info->HandleToProcess                       = wine_server_ptr_handle( data->create_process.process );
+        info->HandleToThread                        = wine_server_ptr_handle( data->create_process.thread );
+        info->NewProcess.FileHandle                 = wine_server_ptr_handle( data->create_process.file );
+        info->NewProcess.BaseOfImage                = wine_server_get_ptr( data->create_process.base );
+        info->NewProcess.DebugInfoFileOffset        = data->create_process.dbg_offset;
+        info->NewProcess.DebugInfoSize              = data->create_process.dbg_size;
+        info->NewProcess.InitialThread.StartAddress = wine_server_get_ptr( data->create_process.start );
+        return STATUS_SUCCESS;
+    }
+    case DbgExitThreadStateChange:
+        state->StateInfo.ExitThread.ExitStatus = data->exit.exit_code;
+        return STATUS_SUCCESS;
+    case DbgExitProcessStateChange:
+        state->StateInfo.ExitProcess.ExitStatus = data->exit.exit_code;
+        return STATUS_SUCCESS;
+    case DbgExceptionStateChange:
+    case DbgBreakpointStateChange:
+    case DbgSingleStepStateChange:
+    {
+        DBGKM_EXCEPTION *info = &state->StateInfo.Exception;
+        info->FirstChance = data->exception.first;
+        info->ExceptionRecord.ExceptionCode    = data->exception.exc_code;
+        info->ExceptionRecord.ExceptionFlags   = data->exception.flags;
+        info->ExceptionRecord.ExceptionRecord  = wine_server_get_ptr( data->exception.record );
+        info->ExceptionRecord.ExceptionAddress = wine_server_get_ptr( data->exception.address );
+        info->ExceptionRecord.NumberParameters = data->exception.nb_params;
+        for (i = 0; i < data->exception.nb_params; i++)
+            info->ExceptionRecord.ExceptionInformation[i] = data->exception.params[i];
+        return STATUS_SUCCESS;
+    }
+    case DbgLoadDllStateChange:
+    {
+        DBGKM_LOAD_DLL *info = &state->StateInfo.LoadDll;
+        info->FileHandle          = wine_server_ptr_handle( data->load_dll.handle );
+        info->BaseOfDll           = wine_server_get_ptr( data->load_dll.base );
+        info->DebugInfoFileOffset = data->load_dll.dbg_offset;
+        info->DebugInfoSize       = data->load_dll.dbg_size;
+        info->NamePointer         = wine_server_get_ptr( data->load_dll.name );
+        return STATUS_SUCCESS;
+    }
+    case DbgUnloadDllStateChange:
+        state->StateInfo.UnloadDll.BaseAddress = wine_server_get_ptr( data->unload_dll.base );
+        return STATUS_SUCCESS;
+    }
+    return STATUS_INTERNAL_ERROR;
+}
+
+/**********************************************************************
+ *           NtWaitForDebugEvent  (NTDLL.@)
+ */
+NTSTATUS WINAPI NtWaitForDebugEvent( HANDLE handle, BOOLEAN alertable, LARGE_INTEGER *timeout,
+                                     DBGUI_WAIT_STATE_CHANGE *state )
+{
+    debug_event_t data;
+    NTSTATUS ret;
+    BOOL wait = TRUE;
+
+    for (;;)
+    {
+        SERVER_START_REQ( wait_debug_event )
+        {
+            req->debug = wine_server_obj_handle( handle );
+            wine_server_set_reply( req, &data, sizeof(data) );
+            ret = wine_server_call( req );
+            if (!ret && !(ret = event_data_to_state_change( &data, state )))
+            {
+                state->NewState = data.code;
+                state->AppClientId.UniqueProcess = ULongToHandle( reply->pid );
+                state->AppClientId.UniqueThread  = ULongToHandle( reply->tid );
+            }
+        }
+        SERVER_END_REQ;
+
+        if (ret != STATUS_PENDING) return ret;
+        if (!wait) return STATUS_TIMEOUT;
+        wait = FALSE;
+        ret = NtWaitForSingleObject( handle, alertable, timeout );
+        if (ret != STATUS_WAIT_0) return ret;
+    }
+}
+
+
 /**************************************************************************
  *           NtCreateDirectoryObject   (NTDLL.@)
  */
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 20cf142e832..a0fe7dbfba1 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -74,7 +74,6 @@ typedef union
     {
         int          code;
         obj_handle_t handle;
-        client_ptr_t teb;
         client_ptr_t start;
     } create_thread;
     struct
@@ -86,10 +85,7 @@ typedef union
         mod_handle_t base;
         int          dbg_offset;
         int          dbg_size;
-        client_ptr_t teb;
         client_ptr_t start;
-        client_ptr_t name;
-        int          unicode;
     } create_process;
     struct
     {
@@ -104,7 +100,6 @@ typedef union
         int          dbg_offset;
         int          dbg_size;
         client_ptr_t name;
-        int          unicode;
     } load_dll;
     struct
     {
@@ -2030,16 +2025,14 @@ struct create_debug_obj_reply
 struct wait_debug_event_request
 {
     struct request_header __header;
-    int           get_handle;
+    obj_handle_t debug;
 };
 struct wait_debug_event_reply
 {
     struct reply_header __header;
     process_id_t  pid;
     thread_id_t   tid;
-    obj_handle_t  wait;
     /* VARARG(event,debug_event); */
-    char __pad_20[4];
 };
 
 
diff --git a/include/winternl.h b/include/winternl.h
index 5523544fd36..c79292b55b0 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -3129,6 +3129,7 @@ NTSYSAPI NTSTATUS  WINAPI DbgUiIssueRemoteBreakin(HANDLE);
 NTSYSAPI void      WINAPI DbgUiRemoteBreakin(void*);
 NTSYSAPI void      WINAPI DbgUiSetThreadDebugObject(HANDLE);
 NTSYSAPI NTSTATUS  WINAPI DbgUiStopDebugging(HANDLE);
+NTSYSAPI NTSTATUS  WINAPI DbgUiWaitStateChange(DBGUI_WAIT_STATE_CHANGE*,LARGE_INTEGER*);
 NTSYSAPI void      WINAPI DbgUserBreakPoint(void);
 NTSYSAPI NTSTATUS  WINAPI LdrAccessResource(HMODULE,const IMAGE_RESOURCE_DATA_ENTRY*,void**,PULONG);
 NTSYSAPI NTSTATUS  WINAPI LdrAddDllDirectory(const UNICODE_STRING*,void**);
@@ -3400,6 +3401,7 @@ NTSYSAPI NTSTATUS  WINAPI NtUnlockFile(HANDLE,PIO_STATUS_BLOCK,PLARGE_INTEGER,PL
 NTSYSAPI NTSTATUS  WINAPI NtUnlockVirtualMemory(HANDLE,PVOID*,SIZE_T*,ULONG);
 NTSYSAPI NTSTATUS  WINAPI NtUnmapViewOfSection(HANDLE,PVOID);
 NTSYSAPI NTSTATUS  WINAPI NtVdmControl(ULONG,PVOID);
+NTSYSAPI NTSTATUS  WINAPI NtWaitForDebugEvent(HANDLE,BOOLEAN,LARGE_INTEGER*,DBGUI_WAIT_STATE_CHANGE*);
 NTSYSAPI NTSTATUS  WINAPI NtWaitForKeyedEvent(HANDLE,const void*,BOOLEAN,const LARGE_INTEGER*);
 NTSYSAPI NTSTATUS  WINAPI NtWaitForSingleObject(HANDLE,BOOLEAN,const LARGE_INTEGER*);
 NTSYSAPI NTSTATUS  WINAPI NtWaitForMultipleObjects(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*);
diff --git a/server/debugger.c b/server/debugger.c
index 32992a733c7..927fe42929e 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -131,10 +131,8 @@ static void fill_exception_event( struct debug_event *event, const void *arg )
 
 static void fill_create_thread_event( struct debug_event *event, const void *arg )
 {
-    struct thread *thread = event->sender;
     const client_ptr_t *entry = arg;
 
-    event->data.create_thread.teb    = thread->teb;
     if (entry) event->data.create_thread.start = *entry;
 }
 
@@ -145,13 +143,10 @@ static void fill_create_process_event( struct debug_event *event, const void *ar
     struct process_dll *exe_module = get_process_exe_module( process );
     const client_ptr_t *entry = arg;
 
-    event->data.create_process.teb        = thread->teb;
     event->data.create_process.base       = exe_module->base;
     event->data.create_process.start      = *entry;
     event->data.create_process.dbg_offset = exe_module->dbg_offset;
     event->data.create_process.dbg_size   = exe_module->dbg_size;
-    event->data.create_process.name       = exe_module->name;
-    event->data.create_process.unicode    = 1;
 
     /* the doc says write access too, but this doesn't seem a good idea */
     event->file = get_mapping_file( process, exe_module->base, GENERIC_READ,
@@ -180,7 +175,6 @@ static void fill_load_dll_event( struct debug_event *event, const void *arg )
     event->data.load_dll.dbg_offset = dll->dbg_offset;
     event->data.load_dll.dbg_size   = dll->dbg_size;
     event->data.load_dll.name       = dll->name;
-    event->data.load_dll.unicode    = 1;
     event->file = get_mapping_file( process, dll->base, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE );
 }
 
@@ -192,17 +186,17 @@ static void fill_unload_dll_event( struct debug_event *event, const void *arg )
 
 typedef void (*fill_event_func)( struct debug_event *event, const void *arg );
 
-#define NB_DEBUG_EVENTS UNLOAD_DLL_DEBUG_EVENT
-
-static const fill_event_func fill_debug_event[NB_DEBUG_EVENTS] =
-{
-    fill_exception_event,            /* EXCEPTION_DEBUG_EVENT */
-    fill_create_thread_event,        /* CREATE_THREAD_DEBUG_EVENT */
-    fill_create_process_event,       /* CREATE_PROCESS_DEBUG_EVENT */
-    fill_exit_thread_event,          /* EXIT_THREAD_DEBUG_EVENT */
-    fill_exit_process_event,         /* EXIT_PROCESS_DEBUG_EVENT */
-    fill_load_dll_event,             /* LOAD_DLL_DEBUG_EVENT */
-    fill_unload_dll_event            /* UNLOAD_DLL_DEBUG_EVENT */
+static const fill_event_func fill_debug_event[] =
+{
+    fill_create_thread_event,  /* DbgCreateThreadStateChange */
+    fill_create_process_event, /* DbgCreateProcessStateChange */
+    fill_exit_thread_event,    /* DbgExitThreadStateChange */
+    fill_exit_process_event,   /* DbgExitProcessStateChange */
+    fill_exception_event,      /* DbgExceptionStateChange */
+    fill_exception_event,      /* DbgBreakpointStateChange */
+    fill_exception_event,      /* DbgSingleStepStateChange */
+    fill_load_dll_event,       /* DbgLoadDllStateChange */
+    fill_unload_dll_event      /* DbgUnloadDllStateChange */
 };
 
 /* allocate the necessary handles in the event data */
@@ -210,11 +204,11 @@ static void alloc_event_handles( struct debug_event *event, struct process *proc
 {
     switch (event->data.code)
     {
-    case CREATE_THREAD_DEBUG_EVENT:
+    case DbgCreateThreadStateChange:
         /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
         event->data.create_thread.handle = alloc_handle( process, event->sender, THREAD_ALL_ACCESS, 0 );
         break;
-    case CREATE_PROCESS_DEBUG_EVENT:
+    case DbgCreateProcessStateChange:
         event->data.create_process.thread = alloc_handle( process, event->sender, THREAD_ALL_ACCESS, 0 );
         /* documented: PROCESS_VM_READ | PROCESS_VM_WRITE */
         event->data.create_process.process = alloc_handle( process, event->sender->process,
@@ -222,7 +216,7 @@ static void alloc_event_handles( struct debug_event *event, struct process *proc
         if (event->file)
             event->data.create_process.file = alloc_handle( process, event->file, GENERIC_READ, 0 );
         break;
-    case LOAD_DLL_DEBUG_EVENT:
+    case DbgLoadDllStateChange:
         if (event->file)
             event->data.load_dll.handle = alloc_handle( process, event->file, GENERIC_READ, 0 );
         break;
@@ -434,7 +428,7 @@ static struct debug_event *alloc_debug_event( struct thread *thread, int code, c
 {
     struct debug_event *event;
 
-    assert( code > 0 && code <= NB_DEBUG_EVENTS );
+    assert( code >= DbgCreateThreadStateChange && code <= DbgUnloadDllStateChange );
 
     /* build the event */
     if (!(event = alloc_object( &debug_event_ops ))) return NULL;
@@ -442,8 +436,7 @@ static struct debug_event *alloc_debug_event( struct thread *thread, int code, c
     event->sender    = (struct thread *)grab_object( thread );
     event->file      = NULL;
     memset( &event->data, 0, sizeof(event->data) );
-
-    fill_debug_event[code-1]( event, arg );
+    fill_debug_event[code - DbgCreateThreadStateChange]( event, arg );
     event->data.code = code;
     return event;
 }
@@ -550,28 +543,28 @@ void generate_startup_debug_events( struct process *process, client_ptr_t entry
     struct list *ptr;
     struct thread *thread, *first_thread = get_process_first_thread( process );
 
-    generate_debug_event( first_thread, CREATE_PROCESS_DEBUG_EVENT, &entry );
+    generate_debug_event( first_thread, DbgCreateProcessStateChange, &entry );
     ptr = list_head( &process->dlls ); /* skip main module reported in create process event */
 
     /* generate ntdll.dll load event */
     if (ptr && (ptr = list_next( &process->dlls, ptr )))
     {
         struct process_dll *dll = LIST_ENTRY( ptr, struct process_dll, entry );
-        generate_debug_event( first_thread, LOAD_DLL_DEBUG_EVENT, dll );
+        generate_debug_event( first_thread, DbgLoadDllStateChange, dll );
     }
 
     /* generate creation events */
     LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
     {
         if (thread != first_thread)
-            generate_debug_event( thread, CREATE_THREAD_DEBUG_EVENT, NULL );
+            generate_debug_event( thread, DbgCreateThreadStateChange, NULL );
     }
 
     /* generate dll events (in loading order) */
     while (ptr && (ptr = list_next( &process->dlls, ptr )))
     {
         struct process_dll *dll = LIST_ENTRY( ptr, struct process_dll, entry );
-        generate_debug_event( first_thread, LOAD_DLL_DEBUG_EVENT, dll );
+        generate_debug_event( first_thread, DbgLoadDllStateChange, dll );
     }
 }
 
@@ -614,15 +607,11 @@ DECL_HANDLER(create_debug_obj)
 /* Wait for a debug event */
 DECL_HANDLER(wait_debug_event)
 {
-    struct debug_obj *debug_obj = current->debug_obj;
+    struct debug_obj *debug_obj;
     struct debug_event *event;
 
-    if (!debug_obj)  /* current thread is not a debugger */
-    {
-        set_error( STATUS_INVALID_HANDLE );
-        return;
-    }
-    reply->wait = 0;
+    if (!(debug_obj = get_debug_obj( current->process, req->debug, DEBUG_READ_EVENT ))) return;
+
     if ((event = find_event_to_send( debug_obj )))
     {
         event->state = EVENT_SENT;
@@ -632,13 +621,12 @@ DECL_HANDLER(wait_debug_event)
         alloc_event_handles( event, current->process );
         set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) ));
     }
-    else  /* no event ready */
+    else
     {
-        reply->pid  = 0;
-        reply->tid  = 0;
-        if (req->get_handle)
-            reply->wait = alloc_handle( current->process, debug_obj, SYNCHRONIZE, 0 );
+        int state = list_empty( &debug_obj->event_queue ) ? DbgIdle : DbgReplyPending;
+        set_reply_data( &state, min( get_reply_max_size(), sizeof(state) ));
     }
+    release_object( debug_obj );
 }
 
 /* Continue a debug event */
@@ -724,7 +712,7 @@ DECL_HANDLER(queue_exception_event)
         data.exception.nb_params = req->len / sizeof(client_ptr_t);
         memcpy( data.exception.params, get_req_data(), req->len );
 
-        if ((event = alloc_debug_event( thread, EXCEPTION_DEBUG_EVENT, &data )))
+        if ((event = alloc_debug_event( thread, DbgExceptionStateChange, &data )))
         {
             if ((reply->handle = alloc_handle( thread->process, event, SYNCHRONIZE, 0 )))
             {
diff --git a/server/process.c b/server/process.c
index bd04c5609c7..95e030777c9 100644
--- a/server/process.c
+++ b/server/process.c
@@ -837,7 +837,7 @@ static void process_unload_dll( struct process *process, mod_handle_t base )
         free( dll->filename );
         list_remove( &dll->entry );
         free( dll );
-        generate_debug_event( current, UNLOAD_DLL_DEBUG_EVENT, &base );
+        generate_debug_event( current, DbgUnloadDllStateChange, &base );
     }
     else set_error( STATUS_INVALID_PARAMETER );
 }
@@ -968,10 +968,10 @@ void remove_process_thread( struct process *process, struct thread *thread )
     {
         /* we have removed the last running thread, exit the process */
         process->exit_code = thread->exit_code;
-        generate_debug_event( thread, EXIT_PROCESS_DEBUG_EVENT, process );
+        generate_debug_event( thread, DbgExitProcessStateChange, process );
         process_killed( process );
     }
-    else generate_debug_event( thread, EXIT_THREAD_DEBUG_EVENT, thread );
+    else generate_debug_event( thread, DbgExitThreadStateChange, thread );
     release_object( thread );
 }
 
@@ -1562,7 +1562,7 @@ DECL_HANDLER(load_dll)
         dll->name       = req->name;
         /* only generate event if initialization is done */
         if (is_process_init_done( current->process ))
-            generate_debug_event( current, LOAD_DLL_DEBUG_EVENT, dll );
+            generate_debug_event( current, DbgLoadDllStateChange, dll );
     }
 }
 
diff --git a/server/protocol.def b/server/protocol.def
index 7fc83435bc5..13edee9ba52 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -76,7 +76,7 @@ typedef union
     int code;   /* event code */
     struct
     {
-        int              code;       /* EXCEPTION_DEBUG_EVENT */
+        int              code;       /* DbgExceptionStateChange */
         int              first;      /* first chance exception? */
         unsigned int     exc_code;   /* exception code */
         unsigned int     flags;      /* exception flags */
@@ -88,43 +88,38 @@ typedef union
     } exception;
     struct
     {
-        int          code;       /* CREATE_THREAD_DEBUG_EVENT */
+        int          code;       /* DbgCreateThreadStateChange */
         obj_handle_t handle;     /* handle to the new thread */
-        client_ptr_t teb;        /* thread teb (in debugged process address space) */
         client_ptr_t start;      /* thread startup routine */
     } create_thread;
     struct
     {
-        int          code;       /* CREATE_PROCESS_DEBUG_EVENT */
+        int          code;       /* DbgCreateProcessStateChange */
         obj_handle_t file;       /* handle to the process exe file */
         obj_handle_t process;    /* handle to the new process */
         obj_handle_t thread;     /* handle to the new thread */
         mod_handle_t base;       /* base of executable image */
         int          dbg_offset; /* offset of debug info in file */
         int          dbg_size;   /* size of debug info */
-        client_ptr_t teb;        /* thread teb (in debugged process address space) */
         client_ptr_t start;      /* thread startup routine */
-        client_ptr_t name;       /* image name (optional) */
-        int          unicode;    /* is it Unicode? */
     } create_process;
     struct
     {
-        int          code;       /* EXIT_THREAD_DEBUG_EVENT/EXIT_PROCESS_DEBUG_EVENT */
+        int          code;       /* DbgExitThreadStateChange/DbgExitProcessStateChange */
         int          exit_code;  /* thread or process exit code */
     } exit;
     struct
     {
-        int          code;       /* LOAD_DLL_DEBUG_EVENT */
+        int          code;       /* DbgLoadDllStateChange */
         obj_handle_t handle;     /* file handle for the dll */
         mod_handle_t base;       /* base address of the dll */
         int          dbg_offset; /* offset of debug info in file */
         int          dbg_size;   /* size of debug info */
         client_ptr_t name;       /* image name (optional) */
-        int          unicode;    /* is it Unicode? */
     } load_dll;
     struct
     {
-        int          code;       /* UNLOAD_DLL_DEBUG_EVENT */
+        int          code;       /* DbgUnloadDllStateChange */
         int          __pad;
         mod_handle_t base;       /* base address of the dll */
     } unload_dll;
@@ -1600,11 +1595,10 @@ struct process_info
 
 /* Wait for a debug event */
 @REQ(wait_debug_event)
-    int           get_handle;  /* should we alloc a handle for waiting? */
+    obj_handle_t debug;        /* debug object */
 @REPLY
     process_id_t  pid;         /* process id */
     thread_id_t   tid;         /* thread id */
-    obj_handle_t  wait;        /* wait handle if no event ready */
     VARARG(event,debug_event); /* debug event data */
 @END
 
diff --git a/server/request.h b/server/request.h
index eb63475a9f2..13e1f663e81 100644
--- a/server/request.h
+++ b/server/request.h
@@ -1138,12 +1138,11 @@ C_ASSERT( FIELD_OFFSET(struct create_debug_obj_request, flags) == 16 );
 C_ASSERT( sizeof(struct create_debug_obj_request) == 24 );
 C_ASSERT( FIELD_OFFSET(struct create_debug_obj_reply, handle) == 8 );
 C_ASSERT( sizeof(struct create_debug_obj_reply) == 16 );
-C_ASSERT( FIELD_OFFSET(struct wait_debug_event_request, get_handle) == 12 );
+C_ASSERT( FIELD_OFFSET(struct wait_debug_event_request, debug) == 12 );
 C_ASSERT( sizeof(struct wait_debug_event_request) == 16 );
 C_ASSERT( FIELD_OFFSET(struct wait_debug_event_reply, pid) == 8 );
 C_ASSERT( FIELD_OFFSET(struct wait_debug_event_reply, tid) == 12 );
-C_ASSERT( FIELD_OFFSET(struct wait_debug_event_reply, wait) == 16 );
-C_ASSERT( sizeof(struct wait_debug_event_reply) == 24 );
+C_ASSERT( sizeof(struct wait_debug_event_reply) == 16 );
 C_ASSERT( FIELD_OFFSET(struct queue_exception_event_request, first) == 12 );
 C_ASSERT( FIELD_OFFSET(struct queue_exception_event_request, code) == 16 );
 C_ASSERT( FIELD_OFFSET(struct queue_exception_event_request, flags) == 20 );
diff --git a/server/thread.c b/server/thread.c
index 296f0848027..fd3538c51f3 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -1449,7 +1449,7 @@ DECL_HANDLER(init_thread)
         if (process->unix_pid != current->unix_pid)
             process->unix_pid = -1;  /* can happen with linuxthreads */
         init_thread_context( current );
-        generate_debug_event( current, CREATE_THREAD_DEBUG_EVENT, &req->entry );
+        generate_debug_event( current, DbgCreateThreadStateChange, &req->entry );
         set_thread_affinity( current, current->affinity );
     }
     debug_level = max( debug_level, req->debug_level );
diff --git a/server/trace.c b/server/trace.c
index 074652b2567..73215ede59f 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -780,60 +780,64 @@ static void dump_varargs_debug_event( const char *prefix, data_size_t size )
 
     switch(event.code)
     {
-    case EXCEPTION_DEBUG_EVENT:
-        fprintf( stderr, "%s{exception,first=%d,exc_code=%08x,flags=%08x", prefix,
-                 event.exception.first, event.exception.exc_code, event.exception.flags );
-        dump_uint64( ",record=", &event.exception.record );
-        dump_uint64( ",address=", &event.exception.address );
-        fprintf( stderr, ",params={" );
-        event.exception.nb_params = min( event.exception.nb_params, EXCEPTION_MAXIMUM_PARAMETERS );
-        for (i = 0; i < event.exception.nb_params; i++)
-        {
-            dump_uint64( "", &event.exception.params[i] );
-            if (i < event.exception.nb_params) fputc( ',', stderr );
-        }
-        fprintf( stderr, "}}" );
+    case DbgIdle:
+        fprintf( stderr, "%s{idle}", prefix );
+        break;
+    case DbgReplyPending:
+        fprintf( stderr, "%s{pending}", prefix );
         break;
-    case CREATE_THREAD_DEBUG_EVENT:
+    case DbgCreateThreadStateChange:
         fprintf( stderr, "%s{create_thread,thread=%04x", prefix, event.create_thread.handle );
-        dump_uint64( ",teb=", &event.create_thread.teb );
         dump_uint64( ",start=", &event.create_thread.start );
         fputc( '}', stderr );
         break;
-    case CREATE_PROCESS_DEBUG_EVENT:
+    case DbgCreateProcessStateChange:
         fprintf( stderr, "%s{create_process,file=%04x,process=%04x,thread=%04x", prefix,
                  event.create_process.file, event.create_process.process,
                  event.create_process.thread );
         dump_uint64( ",base=", &event.create_process.base );
         fprintf( stderr, ",offset=%d,size=%d",
                  event.create_process.dbg_offset, event.create_process.dbg_size );
-        dump_uint64( ",teb=", &event.create_process.teb );
         dump_uint64( ",start=", &event.create_process.start );
-        dump_uint64( ",name=", &event.create_process.name );
-        fprintf( stderr, ",unicode=%d}", event.create_process.unicode );
+        fputc( '}', stderr );
         break;
-    case EXIT_THREAD_DEBUG_EVENT:
+    case DbgExitThreadStateChange:
         fprintf( stderr, "%s{exit_thread,code=%d}", prefix, event.exit.exit_code );
         break;
-    case EXIT_PROCESS_DEBUG_EVENT:
+    case DbgExitProcessStateChange:
         fprintf( stderr, "%s{exit_process,code=%d}", prefix, event.exit.exit_code );
         break;
-    case LOAD_DLL_DEBUG_EVENT:
+    case DbgExceptionStateChange:
+    case DbgBreakpointStateChange:
+    case DbgSingleStepStateChange:
+        fprintf( stderr, "%s{%s,first=%d,exc_code=%08x,flags=%08x", prefix,
+                 event.code == DbgBreakpointStateChange ? "breakpoint" :
+                 event.code == DbgSingleStepStateChange ? "singlestep" : "exception",
+                 event.exception.first, event.exception.exc_code, event.exception.flags );
+        dump_uint64( ",record=", &event.exception.record );
+        dump_uint64( ",address=", &event.exception.address );
+        fprintf( stderr, ",params={" );
+        event.exception.nb_params = min( event.exception.nb_params, EXCEPTION_MAXIMUM_PARAMETERS );
+        for (i = 0; i < event.exception.nb_params; i++)
+        {
+            dump_uint64( "", &event.exception.params[i] );
+            if (i < event.exception.nb_params) fputc( ',', stderr );
+        }
+        fprintf( stderr, "}}" );
+        break;
+    case DbgLoadDllStateChange:
         fprintf( stderr, "%s{load_dll,file=%04x", prefix, event.load_dll.handle );
         dump_uint64( ",base=", &event.load_dll.base );
         fprintf( stderr, ",offset=%d,size=%d",
                  event.load_dll.dbg_offset, event.load_dll.dbg_size );
         dump_uint64( ",name=", &event.load_dll.name );
-        fprintf( stderr, ",unicode=%d}", event.load_dll.unicode );
+        fputc( '}', stderr );
         break;
-    case UNLOAD_DLL_DEBUG_EVENT:
+    case DbgUnloadDllStateChange:
         fprintf( stderr, "%s{unload_dll", prefix );
         dump_uint64( ",base=", &event.unload_dll.base );
         fputc( '}', stderr );
         break;
-    case 0:  /* zero is the code returned on timeouts */
-        fprintf( stderr, "%s{}", prefix );
-        break;
     default:
         fprintf( stderr, "%s{code=??? (%d)}", prefix, event.code );
         break;
@@ -2165,14 +2169,13 @@ static void dump_create_debug_obj_reply( const struct create_debug_obj_reply *re
 
 static void dump_wait_debug_event_request( const struct wait_debug_event_request *req )
 {
-    fprintf( stderr, " get_handle=%d", req->get_handle );
+    fprintf( stderr, " debug=%04x", req->debug );
 }
 
 static void dump_wait_debug_event_reply( const struct wait_debug_event_reply *req )
 {
     fprintf( stderr, " pid=%04x", req->pid );
     fprintf( stderr, ", tid=%04x", req->tid );
-    fprintf( stderr, ", wait=%04x", req->wait );
     dump_varargs_debug_event( ", event=", cur_size );
 }
 
-- 
GitLab