22.1.2 Debugging Functions

Win32 supports a simple, event-driven debugging model. The debugger creates a process to debug using CreateProcess with an appropriate combination of the DEBUG_PROCESS and DEBUG_ONLY_THIS_PROCESS flags.

To debug a process that is already executing, the debugger should use the DebugActiveProcess function, instead of CreateProcess. The debugger must have appropriate access to the executing process to use DebugActiveProcess.

After the debugger has either created or obtained access to the process it intends to debug, the kernel notifies the debugger of all debug events that occur within the process, and possibly within the process' children. For more information on debug events, see Section 0.1.2.2, “Debug Events.”

The debugger uses the WaitForDebugEvent function at the top of its main loop. This function blocks the debugger until a debug event occurs.

When a debug event occurs, the kernel suspends all threads in the process being debugged and notifies the debugger of the event. The debugger can interact with the user, or manipulate the state of the process being debugged by using ReadProcessMemory, WriteProcessMemory, GetThreadContext, or SetThreadContext.

The debugger uses the ContinueDebugEvent function at the bottom of its main loop. This function allows the process being debugged to continue executing.

22.1.2.1 Communicating with the Debugger

Several of the Win32 debugging functions are designed for use within a process that is being debugged, rather than within the debugger.

The OutputDebugString function sends a string to the process that is debugging the current process. The DebugBreak function causes a breakpoint exception in the current process.

The FatalExit function gives execution control to the debugger. The FatalAppExit function terminates the current process. This function should be used only as a last resort, as it does not always free the process' memory or close its files.

22.1.2.2 Debug Events

A debug event is an incident that causes the kernel to notify the debugger. The kernel generates debug events in many circumstances, such as whenever a thread or process is created, whenever a dynamic-link library is loaded or unloaded, and whenever an exception occurs. If a debug event occurs while a debugger is waiting for a debug event, the kernel fills the DEBUG_EVENT structure specified by the WaitForDebugEvent function with information describing the event.

When the kernel generates a debug event, it also suspends all threads in the affected process. The threads will not resume execution until the debugger continues the debug event by using the ContinueDebugEvent function. The following debug events may occur while a process is being debugged:

EXCEPTION_DEBUG_EVENT

The kernel generates this debug event whenever an exception occurs in the process being debugged. Possible exceptions include attempting to access inaccessible memory, executing breakpoint instructions, attempting to divide by zero, or any other exception noted in Chapter 24, “Structured Exception Handling.”

When an EXCEPTION_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes an EXCEPTION_DEBUG_INFO structure. This structure describes the exception that caused the debug event.

In addition to the standard exception conditions, an additional software exception signals the debugger. The kernel generates a DBG_CONTROL_C exception whenever a character-mode process encounters a CTRL+C, if the process uses the default CTRL+C handling mechanism.

CREATE_THREAD_DEBUG_EVENT

The kernel generates this debug event whenever a new thread is created in a process being debugged or whenever the debugger begins debugging an already active process. This debug event is generated before the thread begins to execute in user mode.

When a CREATE_THREAD_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes a CREATE_THREAD_DEBUG_INFO structure. This structure includes a handle to the new thread and the thread's starting address. The handle has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the thread. This enables the debugger to read and write the thread's registers by using the GetThreadContext and SetThreadContext functions. This also enables the debugger to suspend and resume the thread by using the SuspendThread and ResumeThread functions.

CREATE_PROCESS_DEBUG_EVENT

The kernel generates this debug event whenever a new process is created in a process being debugged or whenever the debugger begins debugging an already active process. The kernel generates this debug event before the process begins to execute in user mode and before the kernel generates any other debug events for the new process.

When a CREATE_PROCESS_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes a CREATE_PROCESS_DEBUG_INFO structure. This structure includes a handle to the new process, a handle to the process' image file, a handle to the process' initial thread, and other information that describes the new process.

The handle to the process has PROCESS_VM_READ and PROCESS_VM_WRITE access. This enables the debugger to read and write the process' memory by using the ReadProcessMemory and WriteProcessMemory functions.

The handle to the process' image file has GENERIC_READ access and is opened for read sharing.

The handle to the process' initial thread has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the thread. This enables the debugger to read and write the thread's registers by using the GetThreadContext and SetThreadContext functions. This also enables the debugger to suspend and resume the thread by using the SuspendThread and ResumeThread functions.

EXIT_THREAD_DEBUG_EVENT

The kernel generates this debug event when a thread that is part of a process that is being debugged exits. This occurs immediately after the kernel updates the thread's exit code.

When a EXIT_THREAD_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes a EXIT_THREAD_DEBUG_INFO structure that specifies the exit code.

The debugger should deallocate any internal structures associated with the thread upon receipt of this debug event. The system will close the debugger's handle to the exiting thread.

Note that this debug event does not occur if the exiting thread is the last thread of a process. In this case, EXIT_PROCESS_DEBUG_EVENT occurs instead.

EXIT_PROCESS_DEBUG_EVENT

The kernel generates this debug event when the last thread in a process being debugged exits. This occurs immediately after the kernel unloads the process' DLLs and updates the process' exit code.

When a EXIT_PROCESS_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes a EXIT_PROCESS_DEBUG_INFO structure that specifies the exit code.

The debugger should deallocate any internal structures associated with the process upon receipt of this debug event. The kernel will close the debugger's handle to the exiting process and all of the process' threads.

LOAD_DLL_DEBUG_EVENT

The kernel generates this debug event when a process being debugged loads a DLL. This occurs when the system loader resolves links to a DLL or when the debugged process uses the LoadLibrary function. Note that this debug event only occurs the first time the kernel attaches a DLL to the virtual address space of a process.

When a LOAD_DLL_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes a LOAD_DLL_DEBUG_INFO structure. This structure includes a handle to the newly-loaded DLL, the base address of the DLL, and other information that describes the DLL.

Typically, a debugger will load a symbol table associated with the DLL upon receipt of this debug event.

UNLOAD_DLL_DEBUG_EVENT

The kernel generates this debug event when a process being debugged unloads a DLL. This occurs when the debugged process uses the FreeLibrary function. Note that this debug event only occurs the last time a DLL is unloaded from a process' address space.

When an UNLOAD_DLL_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes an UNLOAD_DLL_DEBUG_INFO structure. This structure specifies the base address of the DLL in the address space of the process unloading the DLL.

Typically, a debugger will unload a symbol table associated with the DLL upon receipt of this debug event.

Note that when a process exits, the kernel automatically unloads the process' DLLs, but does not generate an UNLOAD_DLL_DEBUG_EVENT.

OUTPUT_DEBUG_STRING_EVENT

The kernel generates this debug event when a process being debugged uses the OutputDebugString function.

When an OUTPUT_STRING_DEBUG_EVENT occurs, the DEBUG_EVENT structure specified by WaitForDebugEvent includes an OUTPUT_DEBUG_STRING_INFO structure. This structure specifies the address, length, and format of the debug string.