Author: ion
Date: Thu Oct 19 11:04:21 2006
New Revision: 24572
URL:
http://svn.reactos.org/svn/reactos?rev=24572&view=rev
Log:
- Implement the entire kernel-mode native interface of Debug Objects, minus a few missing
operations in NtWaitForDebugEvent:
- NtCreateDebugObject, NtDebugContinue, NtDebugActiveProcess, NtRemoveProcessDebug,
NtSetInformationDebugObject, NtWaitForDebugEvent.
- Of course, the entire backend is stubbed out.
- Implement Debug object initialization (not called yet) and close(not done) and delete
(done) callbacks.
Modified:
trunk/reactos/include/ddk/ntstatus.h
trunk/reactos/include/ndk/dbgktypes.h
trunk/reactos/ntoskrnl/dbgk/debug.c
Modified: trunk/reactos/include/ddk/ntstatus.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/include/ddk/ntstatus.h?rev…
==============================================================================
--- trunk/reactos/include/ddk/ntstatus.h (original)
+++ trunk/reactos/include/ddk/ntstatus.h Thu Oct 19 11:04:21 2006
@@ -1099,6 +1099,25 @@
#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL ((NTSTATUS)0xC0130016L)
#define STATUS_CLUSTER_POISONED ((NTSTATUS)0xC0130017L)
+/*
+* Debug codes
+*/
+
+#define DBG_EXCEPTION_HANDLED ((NTSTATUS)0x00010001)
+#define DBG_CONTINUE ((NTSTATUS)0x00010002)
+#define DBG_REPLY_LATER ((NTSTATUS)0x40010001)
+#define DBG_UNABLE_TO_PROVIDE_HANDLE ((NTSTATUS)0x40010002)
+#define DBG_TERMINATE_THREAD ((NTSTATUS)0x40010003)
+#define DBG_TERMINATE_PROCESS ((NTSTATUS)0x40010004)
+#define DBG_CONTROL_C ((NTSTATUS)0x40010005)
+#define DBG_PRINTEXCEPTION_C ((NTSTATUS)0x40010006)
+#define DBG_RIPEXCEPTION ((NTSTATUS)0x40010007)
+#define DBG_CONTROL_BREAK ((NTSTATUS)0x40010008)
+#define DBG_COMMAND_EXCEPTION ((NTSTATUS)0x40010009)
+#define DBG_EXCEPTION_NOT_HANDLED ((NTSTATUS)0x80010001)
+#define DBG_NO_STATE_CHANGE ((NTSTATUS)0xC0010001)
+#define DBG_APP_NOT_IDLE ((NTSTATUS)0xC0010002)
+
#ifdef __cplusplus
}
#endif
Modified: trunk/reactos/include/ndk/dbgktypes.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/include/ndk/dbgktypes.h?re…
==============================================================================
--- trunk/reactos/include/ndk/dbgktypes.h (original)
+++ trunk/reactos/include/ndk/dbgktypes.h Thu Oct 19 11:04:21 2006
@@ -30,6 +30,7 @@
//
#define DEBUG_OBJECT_WAIT_STATE_CHANGE 0x0001
#define DEBUG_OBJECT_ADD_REMOVE_PROCESS 0x0002
+#define DEBUG_OBJECT_SET_INFORMATION 0x0004
#define DEBUG_OBJECT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE |
0x0F)
//
@@ -172,8 +173,8 @@
typedef struct _DBGKM_MSG
{
PORT_MESSAGE h;
- ULONG Opcode;
- ULONG Status;
+ ULONG ApiNumber;
+ ULONG ReturnedStatus;
union
{
DBGKM_EXCEPTION Exception;
@@ -186,4 +187,25 @@
};
} DBGKM_MSG, *PDBGKM_MSG;
+#ifndef NTOS_MODE_USER
+
+//
+// Debug Event
+//
+typedef struct _DEBUG_EVENT
+{
+ LIST_ENTRY EventList;
+ KEVENT ContinueEvent;
+ CLIENT_ID ClientId;
+ PEPROCESS Process;
+ PETHREAD Thread;
+ NTSTATUS Status;
+ ULONG Flags;
+ PETHREAD BackoutThread;
+ DBGKM_MSG ApiMsg;
+} DEBUG_EVENT, *PDEBUG_EVENT;
+
+
#endif
+
+#endif
Modified: trunk/reactos/ntoskrnl/dbgk/debug.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/dbgk/debug.c?rev=…
==============================================================================
--- trunk/reactos/ntoskrnl/dbgk/debug.c (original)
+++ trunk/reactos/ntoskrnl/dbgk/debug.c Thu Oct 19 11:04:21 2006
@@ -7,15 +7,76 @@
* PROGRAMMERS: Alex Ionescu (alex(a)relsoft.net)
*/
-/* INCLUDES *****************************************************************/
+/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
POBJECT_TYPE DbgkDebugObjectType;
-
-/* FUNCTIONS *****************************************************************/
+KGUARDED_MUTEX DbgkpProcessDebugPortMutex;
+
+GENERIC_MAPPING DbgkDebugObjectMapping =
+{
+ STANDARD_RIGHTS_READ | DEBUG_OBJECT_WAIT_STATE_CHANGE,
+ STANDARD_RIGHTS_WRITE | DEBUG_OBJECT_ADD_REMOVE_PROCESS,
+ STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
+ DEBUG_OBJECT_ALL_ACCESS
+};
+
+/* PRIVATE FUNCTIONS *********************************************************/
+
+VOID
+NTAPI
+DbgkpDeleteObject(IN PVOID Object)
+{
+ PDBGK_DEBUG_OBJECT DebugObject = Object;
+ PAGED_CODE();
+
+ /* Sanity check */
+ ASSERT(IsListEmpty(&DebugObject->StateEventListEntry));
+}
+
+VOID
+NTAPI
+DbgkpCloseObject(IN PEPROCESS Process OPTIONAL,
+ IN PVOID ObjectBody,
+ IN ACCESS_MASK GrantedAccess,
+ IN ULONG HandleCount,
+ IN ULONG SystemHandleCount)
+{
+ /* FIXME: Implement */
+ ASSERT(FALSE);
+}
+
+VOID
+INIT_FUNCTION
+NTAPI
+DbgkInitialize(VOID)
+{
+ OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
+ UNICODE_STRING Name;
+ PAGED_CODE();
+
+ /* Initialize the process debug port mutex */
+ KeInitializeGuardedMutex(&DbgkpProcessDebugPortMutex);
+
+ /* Create the Event Pair Object Type */
+ RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
+ RtlInitUnicodeString(&Name, L"DebugObject");
+ ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
+ ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(DBGK_DEBUG_OBJECT);
+ ObjectTypeInitializer.GenericMapping = DbgkDebugObjectMapping;
+ ObjectTypeInitializer.PoolType = NonPagedPool;
+ ObjectTypeInitializer.ValidAccessMask = DEBUG_OBJECT_WAIT_STATE_CHANGE;
+ ObjectTypeInitializer.UseDefaultObject = TRUE;
+ ObjectTypeInitializer.CloseProcedure = DbgkpCloseObject;
+ ObjectTypeInitializer.DeleteProcedure = DbgkpDeleteObject;
+ ObCreateObjectType(&Name,
+ &ObjectTypeInitializer,
+ NULL,
+ &DbgkDebugObjectType);
+}
VOID
NTAPI
@@ -35,6 +96,66 @@
return FALSE;
}
+VOID
+NTAPI
+DbgkpWakeTarget(IN PDEBUG_EVENT DebugEvent)
+{
+ /* FIXME: TODO */
+ return;
+}
+
+NTSTATUS
+NTAPI
+DbgkpPostFakeProcessCreateMessages(IN PEPROCESS Process,
+ IN PDBGK_DEBUG_OBJECT DebugObject,
+ IN PETHREAD *LastThread)
+{
+ /* FIXME: Implement */
+ *LastThread = NULL;
+ return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS
+NTAPI
+DbgkpSetProcessDebugObject(IN PEPROCESS Process,
+ IN PDBGK_DEBUG_OBJECT DebugObject,
+ IN NTSTATUS MsgStatus,
+ IN PETHREAD LastThread)
+{
+ /* FIXME: TODO */
+ return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS
+NTAPI
+DbgkClearProcessDebugObject(IN PEPROCESS Process,
+ IN PDBGK_DEBUG_OBJECT SourceDebugObject)
+{
+ /* FIXME: TODO */
+ return STATUS_UNSUCCESSFUL;
+}
+
+VOID
+NTAPI
+DbgkpConvertKernelToUserStateChange(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
+ IN PDEBUG_EVENT DebugEvent)
+{
+ /* FIXME: TODO */
+ return;
+}
+
+VOID
+NTAPI
+DbgkpOpenHandles(IN PDBGUI_WAIT_STATE_CHANGE WaitStateChange,
+ IN PEPROCESS Process,
+ IN PETHREAD Thread)
+{
+ /* FIXME: TODO */
+ return;
+}
+
+
+/* PUBLIC FUNCTIONS **********************************************************/
NTSTATUS
NTAPI
@@ -47,25 +168,23 @@
PDBGK_DEBUG_OBJECT DebugObject;
HANDLE hDebug;
NTSTATUS Status = STATUS_SUCCESS;
-
PAGED_CODE();
- DPRINT("NtCreateDebugObject(0x%p, 0x%x, 0x%p)\n", DebugHandle,
DesiredAccess, ObjectAttributes);
-
- /* Check Output Safety */
- if(PreviousMode != KernelMode) {
-
- _SEH_TRY {
-
- ProbeForWrite(DebugHandle,
- sizeof(HANDLE),
- sizeof(ULONG));
- } _SEH_HANDLE {
-
+
+ /* Check if we were called from user mode*/
+ if (PreviousMode != KernelMode)
+ {
+ /* Enter SEH for probing */
+ _SEH_TRY
+ {
+ /* Probe the handle */
+ ProbeForWrite(DebugHandle, sizeof(HANDLE), sizeof(ULONG));
+ }
+ _SEH_HANDLE
+ {
+ /* Get exception error */
Status = _SEH_GetExceptionCode();
-
} _SEH_END;
-
- if(!NT_SUCCESS(Status)) return Status;
+ if (!NT_SUCCESS(Status)) return Status;
}
/* Create the Object */
@@ -78,10 +197,8 @@
0,
0,
(PVOID*)&DebugObject);
-
- /* Check for Success */
- if(NT_SUCCESS(Status)) {
-
+ if (NT_SUCCESS(Status))
+ {
/* Initialize the Debug Object's Fast Mutex */
ExInitializeFastMutex(&DebugObject->Mutex);
@@ -104,16 +221,15 @@
ObDereferenceObject(DebugObject);
/* Check for success and return handle */
- if(NT_SUCCESS(Status)) {
-
- _SEH_TRY {
-
+ if (NT_SUCCESS(Status))
+ {
+ _SEH_TRY
+ {
*DebugHandle = hDebug;
-
- } _SEH_HANDLE {
-
+ }
+ _SEH_HANDLE
+ {
Status = _SEH_GetExceptionCode();
-
} _SEH_END;
}
}
@@ -124,61 +240,507 @@
NTSTATUS
NTAPI
-NtWaitForDebugEvent(IN HANDLE DebugObject, // Debug object handle must grant
DEBUG_OBJECT_WAIT_STATE_CHANGE access.
- IN BOOLEAN Alertable,
- IN PLARGE_INTEGER Timeout OPTIONAL,
- OUT PDBGUI_WAIT_STATE_CHANGE StateChange)
-{
-
- UNIMPLEMENTED;
-
- return STATUS_NOT_IMPLEMENTED;
-}
-
-NTSTATUS
-NTAPI
-NtDebugContinue(IN HANDLE DebugObject, // Debug object handle must grant
DEBUG_OBJECT_WAIT_STATE_CHANGE access.
+NtDebugContinue(IN HANDLE DebugHandle,
IN PCLIENT_ID AppClientId,
IN NTSTATUS ContinueStatus)
{
-
- UNIMPLEMENTED;
-
- return STATUS_NOT_IMPLEMENTED;
-}
-
-NTSTATUS
-NTAPI
-NtDebugActiveProcess(IN HANDLE Process, // Process handle must grant
PROCESS_SUSPEND_RESUME access.
- IN HANDLE DebugObject) // Debug object handle must grant
DEBUG_OBJECT_ADD_REMOVE_PROCESS access.
-{
-
- UNIMPLEMENTED;
-
- return STATUS_NOT_IMPLEMENTED;
-}
-
-NTSTATUS
-NTAPI
-NtRemoveProcessDebug(IN HANDLE Process, // Process handle must grant
PROCESS_SUSPEND_RESUME access.
- IN HANDLE DebugObject) // Debug object handle must grant
DEBUG_OBJECT_ADD_REMOVE_PROCESS access.
-{
-
- UNIMPLEMENTED;
-
- return STATUS_NOT_IMPLEMENTED;
-}
-
-NTSTATUS
-NTAPI
-NtSetInformationDebugObject(IN HANDLE DebugObject, // Debug object handle need not grant
any particular access right.
+ KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+ PDBGK_DEBUG_OBJECT DebugObject;
+ NTSTATUS Status = STATUS_SUCCESS;
+ PDEBUG_EVENT DebugEvent = NULL, DebugEventToWake = NULL;
+ PLIST_ENTRY ListHead, NextEntry;
+ BOOLEAN NeedsWake = FALSE;
+ CLIENT_ID ClientId;
+ PAGED_CODE();
+
+ /* Check if we were called from user mode*/
+ if (PreviousMode != KernelMode)
+ {
+ /* Enter SEH for probing */
+ _SEH_TRY
+ {
+ /* Probe the handle */
+ ProbeForRead(AppClientId, sizeof(CLIENT_ID), sizeof(ULONG));
+ ClientId = *AppClientId;
+ }
+ _SEH_HANDLE
+ {
+ /* Get exception error */
+ Status = _SEH_GetExceptionCode();
+ } _SEH_END;
+ if (!NT_SUCCESS(Status)) return Status;
+ }
+
+ /* Make sure that the status is valid */
+ if ((ContinueStatus != DBG_EXCEPTION_NOT_HANDLED) &&
+ (ContinueStatus != DBG_REPLY_LATER) &&
+ (ContinueStatus != DBG_UNABLE_TO_PROVIDE_HANDLE) &&
+ (ContinueStatus != DBG_TERMINATE_THREAD) &&
+ (ContinueStatus != DBG_TERMINATE_PROCESS))
+ {
+ /* Invalid status */
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ /* Get the debug object */
+ Status = ObReferenceObjectByHandle(DebugHandle,
+ DEBUG_OBJECT_WAIT_STATE_CHANGE,
+ DbgkDebugObjectType,
+ PreviousMode,
+ (PVOID*)&DebugObject,
+ NULL);
+ if (NT_SUCCESS(Status))
+ {
+ /* Acquire the mutex */
+ ExAcquireFastMutex(&DebugObject->Mutex);
+
+ /* Loop the state list */
+ ListHead = &DebugObject->StateEventListEntry;
+ NextEntry = ListHead->Flink;
+ while (ListHead != NextEntry)
+ {
+ /* Get the current debug event */
+ DebugEvent = CONTAINING_RECORD(NextEntry,
+ DEBUG_EVENT,
+ EventList);
+
+ /* Compare process ID */
+ if (DebugEvent->ClientId.UniqueProcess ==
+ ClientId.UniqueProcess)
+ {
+ /* Check if we already found a match */
+ if (NeedsWake)
+ {
+ /* Wake it up and break out */
+ DebugEvent->Flags &= ~4;
+ KeSetEvent(&DebugEvent->ContinueEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+ break;
+ }
+
+ /* Compare thread ID and flag */
+ if ((DebugEvent->ClientId.UniqueThread ==
+ ClientId.UniqueThread) && (DebugEvent->Flags &
1))
+ {
+ /* Remove the event from the list */
+ RemoveEntryList(NextEntry);
+
+ /* Remember who to wake */
+ NeedsWake = TRUE;
+ DebugEventToWake = DebugEvent;
+ }
+ }
+
+ /* Go to the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Release the mutex */
+ ExReleaseFastMutex(&DebugObject->Mutex);
+
+ /* Dereference the object */
+ ObDereferenceObject(DebugObject);
+
+ /* Check if need a wait */
+ if (NeedsWake)
+ {
+ /* Set the continue status */
+ DebugEvent->ApiMsg.ReturnedStatus = Status;
+ DebugEvent->Status = STATUS_SUCCESS;
+
+ /* Wake the target */
+ DbgkpWakeTarget(DebugEvent);
+ }
+ else
+ {
+ /* Fail */
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ /* Return status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+NtDebugActiveProcess(IN HANDLE ProcessHandle,
+ IN HANDLE DebugHandle)
+{
+ PEPROCESS Process;
+ PDBGK_DEBUG_OBJECT DebugObject;
+ KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
+ PETHREAD LastThread;
+ NTSTATUS Status;
+
+ /* Reference the process */
+ Status = ObReferenceObjectByHandle(ProcessHandle,
+ PROCESS_SUSPEND_RESUME,
+ PsProcessType,
+ PreviousMode,
+ (PVOID*)&Process,
+ NULL);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Don't allow debugging the initial system process */
+ if (Process == PsInitialSystemProcess) return STATUS_ACCESS_DENIED;
+
+ /* Reference the debug object */
+ Status = ObReferenceObjectByHandle(DebugHandle,
+ DEBUG_OBJECT_ADD_REMOVE_PROCESS,
+ DbgkDebugObjectType,
+ PreviousMode,
+ (PVOID*)&DebugObject,
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Dereference the process and exit */
+ ObDereferenceObject(Process);
+ return Status;
+ }
+
+ /* Acquire process rundown protection */
+ if (!ExAcquireRundownProtection(&Process->RundownProtect))
+ {
+ /* Dereference the process and debug object and exit */
+ ObDereferenceObject(Process);
+ ObDereferenceObject(DebugObject);
+ return STATUS_PROCESS_IS_TERMINATING;
+ }
+
+ /* Send fake create messages for debuggers to have a consistent state */
+ Status = DbgkpPostFakeProcessCreateMessages(Process,
+ DebugObject,
+ &LastThread);
+ Status = DbgkpSetProcessDebugObject(Process,
+ DebugObject,
+ Status,
+ LastThread);
+
+ /* Release rundown protection */
+ ExReleaseRundownProtection(&Process->RundownProtect);
+
+ /* Dereference the process and debug object and return status */
+ ObDereferenceObject(Process);
+ ObDereferenceObject(DebugObject);
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+NtRemoveProcessDebug(IN HANDLE ProcessHandle,
+ IN HANDLE DebugHandle)
+{
+ PEPROCESS Process;
+ PDBGK_DEBUG_OBJECT DebugObject;
+ KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
+ NTSTATUS Status;
+
+ /* Reference the process */
+ Status = ObReferenceObjectByHandle(ProcessHandle,
+ PROCESS_SUSPEND_RESUME,
+ PsProcessType,
+ PreviousMode,
+ (PVOID*)&Process,
+ NULL);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Reference the debug object */
+ Status = ObReferenceObjectByHandle(DebugHandle,
+ DEBUG_OBJECT_ADD_REMOVE_PROCESS,
+ DbgkDebugObjectType,
+ PreviousMode,
+ (PVOID*)&DebugObject,
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Dereference the process and exit */
+ ObDereferenceObject(Process);
+ return Status;
+ }
+
+ /* Remove the debug object */
+ Status = DbgkClearProcessDebugObject(Process, DebugObject);
+
+ /* Dereference the process and debug object and return status */
+ ObDereferenceObject(Process);
+ ObDereferenceObject(DebugObject);
+ return Status;
+}
+
+static const INFORMATION_CLASS_INFO DbgkpDebugObjectInfoClass[] =
+{
+ /* DebugObjectUnusedInformation */
+ ICI_SQ_SAME(sizeof(ULONG), sizeof(ULONG), 0),
+ /* DebugObjectKillProcessOnExitInformation */
+ ICI_SQ_SAME(sizeof(DEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION), sizeof(ULONG),
ICIF_SET),
+};
+
+NTSTATUS
+NTAPI
+NtSetInformationDebugObject(IN HANDLE DebugHandle,
IN DEBUGOBJECTINFOCLASS DebugObjectInformationClass,
IN PVOID DebugInformation,
IN ULONG DebugInformationLength,
OUT PULONG ReturnLength OPTIONAL)
{
- UNIMPLEMENTED;
-
- return STATUS_NOT_IMPLEMENTED;
-}
+ PDBGK_DEBUG_OBJECT DebugObject;
+ KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+ NTSTATUS Status = STATUS_SUCCESS;
+ PDEBUG_OBJECT_KILL_PROCESS_ON_EXIT_INFORMATION DebugInfo = DebugInformation;
+ PAGED_CODE();
+
+ /* Check buffers and parameters */
+ Status = DefaultSetInfoBufferCheck(DebugObjectInformationClass,
+ DbgkpDebugObjectInfoClass,
+ sizeof(DbgkpDebugObjectInfoClass) /
+ sizeof(DbgkpDebugObjectInfoClass[0]),
+ DebugInformation,
+ DebugInformationLength,
+ PreviousMode);
+
+ /* Return required length to user-mode */
+ if (ReturnLength) *ReturnLength = sizeof(*DebugInfo);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Open the Object */
+ Status = ObReferenceObjectByHandle(DebugHandle,
+ DEBUG_OBJECT_WAIT_STATE_CHANGE,
+ DbgkDebugObjectType,
+ PreviousMode,
+ (PVOID*)&DebugObject,
+ NULL);
+ if (NT_SUCCESS(Status))
+ {
+ /* Acquire the object */
+ ExAcquireFastMutex(&DebugObject->Mutex);
+
+ /* Set the proper flag */
+ if (DebugInfo->KillProcessOnExit)
+ {
+ /* Enable killing the process */
+ DebugObject->Flags |= 2;
+ }
+ else
+ {
+ /* Disable */
+ DebugObject->Flags &= ~2;
+ }
+
+ /* Release the mutex */
+ ExReleaseFastMutex(&DebugObject->Mutex);
+
+ /* Release the Object */
+ ObDereferenceObject(DebugObject);
+ }
+
+ /* Return Status */
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+NtWaitForDebugEvent(IN HANDLE DebugHandle,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Timeout OPTIONAL,
+ OUT PDBGUI_WAIT_STATE_CHANGE StateChange)
+{
+ KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+ LARGE_INTEGER SafeTimeOut;
+ PEPROCESS Process;
+ LARGE_INTEGER StartTime;
+ PETHREAD Thread;
+ BOOLEAN GotEvent;
+ LARGE_INTEGER NewTime;
+ PDBGK_DEBUG_OBJECT DebugObject;
+ DBGUI_WAIT_STATE_CHANGE WaitStateChange;
+ NTSTATUS Status = STATUS_SUCCESS;
+ PDEBUG_EVENT DebugEvent, DebugEvent2;
+ PLIST_ENTRY ListHead, NextEntry;
+
+ /* Clear the initial wait state change structure */
+ RtlZeroMemory(&WaitStateChange, sizeof(WaitStateChange));
+
+ /* Check if we came with a timeout from user mode */
+ if ((Timeout) && (PreviousMode != KernelMode))
+ {
+ _SEH_TRY
+ {
+ /* Make a copy on the stack */
+ SafeTimeOut = ProbeForReadLargeInteger(Timeout);
+ Timeout = &SafeTimeOut;
+ }
+ _SEH_HANDLE
+ {
+ /* Get the exception code */
+ Status = _SEH_GetExceptionCode();
+ }
+ _SEH_END;
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Query the current time */
+ KeQuerySystemTime(&StartTime);
+ }
+
+ /* Check if the call is from user mode */
+ if (PreviousMode == UserMode)
+ {
+ /* FIXME: Probe the state change structure */
+ }
+
+ /* Get the debug object */
+ Status = ObReferenceObjectByHandle(DebugHandle,
+ DEBUG_OBJECT_WAIT_STATE_CHANGE,
+ DbgkDebugObjectType,
+ PreviousMode,
+ (PVOID*)&DebugObject,
+ NULL);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Clear process and thread */
+ Process = NULL;
+ Thread = NULL;
+
+ /* Wait on the debug object given to us */
+ Status = KeWaitForSingleObject(DebugObject,
+ Executive,
+ PreviousMode,
+ Alertable,
+ Timeout);
+
+ /* Start the wait loop */
+ while (TRUE)
+ {
+ if (!NT_SUCCESS(Status) ||
+ (Status == STATUS_TIMEOUT) ||
+ (Status == STATUS_ALERTED) ||
+ (Status == STATUS_USER_APC))
+ {
+ /* Break out the wait */
+ break;
+ }
+
+ /* Lock the object */
+ GotEvent = FALSE;
+ ExAcquireFastMutex(&DebugObject->Mutex);
+
+ /* Check if a debugger is connected */
+ if (DebugObject->Flags & 1)
+ {
+ /* Not connected */
+ Status = STATUS_DEBUGGER_INACTIVE;
+ }
+ else
+ {
+ /* Loop the events */
+ ListHead = &DebugObject->StateEventListEntry;
+ NextEntry = ListHead->Flink;
+ while (ListHead != NextEntry)
+ {
+ /* Get the debug event */
+ DebugEvent = CONTAINING_RECORD(NextEntry,
+ DEBUG_EVENT,
+ EventList);
+
+ /* Check flags */
+ if (!(DebugEvent->Flags & (4 | 1)))
+ {
+ /* We got an event */
+ GotEvent = TRUE;
+
+ /* Loop the list internally */
+ while (&DebugEvent->EventList != NextEntry)
+ {
+ /* Get the debug event */
+ DebugEvent2 = CONTAINING_RECORD(NextEntry,
+ DEBUG_EVENT,
+ EventList);
+
+ /* Try to match process IDs */
+ if (DebugEvent2->ClientId.UniqueProcess ==
+ DebugEvent->ClientId.UniqueProcess)
+ {
+ /* Found it, break out */
+ DebugEvent->Flags |= 4;
+ DebugEvent->BackoutThread = NULL;
+ GotEvent = FALSE;
+ break;
+ }
+
+ /* Move to the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Check if we still have a valid event */
+ if (GotEvent) break;
+ }
+
+ /* Move to the next entry */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Check if we have an event */
+ if (GotEvent)
+ {
+ /* Save and reference the process and thread */
+ Process = DebugEvent->Process;
+ Thread = DebugEvent->Thread;
+ ObReferenceObject(Process);
+ ObReferenceObject(Thread);
+
+ /* Convert to user-mode structure */
+ DbgkpConvertKernelToUserStateChange(&WaitStateChange,
+ DebugEvent);
+
+ /* Set flag */
+ DebugEvent->Flags |= 1;
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+ /* Unsignal the event */
+ DebugObject->Event.Header.SignalState = 0;
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ /* Release the mutex */
+ ExReleaseFastMutex(&DebugObject->Mutex);
+ if (!NT_SUCCESS(Status)) break;
+
+ /* Check if we got an event */
+ if (GotEvent)
+ {
+ /* Check if we can wait again */
+ if (!SafeTimeOut.QuadPart)
+ {
+ /* Query the new time */
+ KeQuerySystemTime(&NewTime);
+
+ /* Substract times */
+ /* FIXME: TODO */
+ }
+ }
+ else
+ {
+ /* Open the handles and dereference the objects */
+ DbgkpOpenHandles(&WaitStateChange, Process, Thread);
+ ObDereferenceObject(Process);
+ ObDereferenceObject(Thread);
+ }
+ }
+
+ /* We're, dereference the object */
+ ObDereferenceObject(DebugObject);
+
+ /* Return our wait state change structure */
+ RtlMoveMemory(StateChange,
+ &WaitStateChange,
+ sizeof(DBGUI_WAIT_STATE_CHANGE));
+ return Status;
+}
+
/* EOF */