调试事件

调试事件是指在正在调试的进程中发生的会导致系统通知调试器的事件。 调试事件包括创建进程、创建线程、加载动态链接库 (DLL)、卸载 DLL、发送输出字符串以及生成异常。

如果在调试器在等待调试事件时发生了调试事件,则系统会使用描述该事件的信息填充 WaitForDebugEvent 指定的 DEBUG_EVENT 结构。

当系统通知调试器发生调试事件时,它还会将受影响进程中的所有线程挂起。 在调试器使用 ContinueDebugEvent 继续调试事件之前,线程不会恢复执行。 在调试进程时,可能会发生以下调试事件。

调试事件 说明
CREATE_PROCESS_DEBUG_EVENT
每当在正在调试的进程中创建新进程或每当调试器开始调试已处于活动状态的进程时生成。 系统会在进程开始在用户模式下执行之前生成此调试事件,也会在系统为新进程生成任何其他调试事件之前生成此调试事件。
DEBUG_EVENT 结构包含 CREATE_PROCESS_DEBUG_INFO 结构。 此结构包括新进程的句柄、进程映像文件的句柄、进程初始线程的句柄,以及描述新进程的其他信息。
进程的句柄具有 PROCESS_VM_READ 和 PROCESS_VM_WRITE 访问权限。 如果调试器对线程具有这些类型的访问权限,则可以使用 ReadProcessMemory 函数和 WriteProcessMemory 函数读取和写入进程内存。 如果系统以前报告过 EXIT_PROCESS_DEBUG_EVENT 事件,则当调试器调用 ContinueDebugEvent 函数时,系统将关闭此句柄。
进程映像文件的句柄具有 GENERIC_READ 访问权限,并可将其打开来进行读取共享。 在处理 CREATE_PROCESS_DEBUG_EVENT 时,调试器应关闭此句柄。
进程初始线程的句柄对线程具有 THREAD_GET_CONTEXT、THREAD_SET_CONTEXT 和 THREAD_SUSPEND_RESUME 访问权限。 如果调试器对线程具有这些类型的访问权限,则可以使用 GetThreadContext 函数和 SetThreadContext 函数读取和写入线程寄存器,并且可以使用 SuspendThread 函数和 ResumeThread 函数暂停和恢复线程。 如果系统以前报告过 EXIT_PROCESS_DEBUG_EVENT 事件,则当调试器调用 ContinueDebugEvent 函数时,系统将关闭此句柄。
CREATE_THREAD_DEBUG_EVENT
每当在正在调试的进程中创建新线程或每当调试器开始调试已处于活动状态的进程时生成。 此调试事件是在新线程开始在用户模式下执行之前生成的。
DEBUG_EVENT 结构包含 CREATE_THREAD_DEBUG_INFO 结构。 此结构包括新线程的句柄和该线程的起始地址。 句柄具有对线程的 THREAD_GET_CONTEXT、THREAD_SET_CONTEXT 和 THREAD_SUSPEND_RESUME 访问权限。 如果调试器对线程具有这些类型的访问权限,则可以使用 GetThreadContext 函数和 SetThreadContext 函数读取和写入线程寄存器,并且可以使用 SuspendThread 函数和 ResumeThread 函数暂停和恢复线程。
如果系统以前报告过 EXIT_THREAD_DEBUG_EVENT 事件,则当调试器调用 ContinueDebugEvent 函数时,系统将关闭新线程的句柄。
EXCEPTION_DEBUG_EVENT
每当在正在调试的进程中发生异常时生成。 可能的异常包括尝试访问不可访问的内存、执行断点指令、尝试除以零,或者结构化异常处理中记录的任何其他异常。
DEBUG_EVENT 结构包含 EXCEPTION_DEBUG_INFO 结构。 此结构描述导致产生调试事件的异常。
除了标准异常条件外,控制台进程调试期间还可能会出现其他异常代码。 将 CTRL+C 输入到处理 CTRL+C 信号且正在调试的控制台进程时,系统将生成 DBG_CONTROL_C 异常代码。 应用程序不会处理此异常代码。 应用程序绝不应该使用异常处理程序来处理此异常代码。 此异常代码就是专门针对调试器引发的,并且仅在将调试器连接到控制台进程时才会使用此异常代码。
如果未在调试进程,或者调试器(通过 gn 命令)将未经处理的 DBG_CONTROL_C 异常往下传递,将搜索应用程序的处理程序函数列表,如 SetConsoleCtrlHandler 函数相关内容所述。
如果调试器(通过 gh 命令)处理 DBG_CONTROL_C 异常,则除非是像代码中这样,否则应用程序不会注意到 CTRL+C。
while ((inputChar = getchar()) != EOF) ...
因此,不能将调试器用于阻止此类代码中的读取等待终止。
EXIT_PROCESS_DEBUG_EVENT
每当正在调试的进程中的最后一个线程退出时生成。 在系统卸载进程 DLL 并更新进程退出代码后,将立即发生此调试事件。
DEBUG_EVENT 结构包含指定退出代码的 EXIT_PROCESS_DEBUG_INFO 结构。
在收到此调试事件时,调试器将解除分配任何与进程关联的内部结构。 系统将关闭调试器对退出进程和该进程的所有线程的句柄。 调试器不应关闭这些句柄。
在收到此事件的调试器调用 ContinueDebugEvent 之前,无法完成进程关闭的内核模式部分。 在此之前,进程句柄将处于打开状态,并且不会释放虚拟地址空间,因此调试器可以检查子进程。 若要在进程关闭的内核模式部分完成时收到通知,请复制使用 CREATE_PROCESS_DEBUG_EVENT 返回的句柄,调用 ContinueDebugEvent,然后等待系统发出进程句柄已复制的信号。
EXIT_THREAD_DEBUG_EVENT
每当作为正在调试的进程一部分的线程退出时生成。 在更新线程的退出代码后,系统会立即生成此调试事件。
DEBUG_EVENT 结构包含指定退出代码的 EXIT_THREAD_DEBUG_INFO 结构。
如果退出线程是进程的最后一个线程,则不会发生此调试事件。 在这种情况下,将改为发生 EXIT_PROCESS_DEBUG_EVENT 调试事件。
在收到此调试事件时,调试器将解除分配任何与线程关联的内部结构。 系统将关闭调试器对退出线程的句柄。 调试器不应关闭此句柄。
LOAD_DLL_DEBUG_EVENT
每当正在调试的进程加载 DLL 时生成。 当系统加载程序解析指向 DLL 的链接,或者调试的进程使用 LoadLibrary 函数时,会发生此调试事件。 仅在系统首次将 DLL 连接到进程的虚拟地址空间时,才会发生此调试事件。
DEBUG_EVENT 结构包含 LOAD_DLL_DEBUG_INFO 结构。 此结构包括新加载的 DLL 的句柄、DLL 的基址以及描述 DLL 的其他信息。 在处理 LOAD_DLL_DEBUG_EVENT 时,调试器应关闭 DLL 句柄。
通常,调试器会在收到此调试事件时加载与 DLL 关联的符号表。
OUTPUT_DEBUG_STRING_EVENT
在正在调试的进程使用以下项时生成:
OutputDebugString 函数。 DEBUG_EVENT 结构包含 OUTPUT_DEBUG_STRING_INFO 结构。 此结构指定调试字符串的地址、长度和格式。
UNLOAD_DLL_DEBUG_EVENT
每当正在调试的进程使用 FreeLibrary 函数卸载 DLL 时生成。 仅在最后一次从进程的地址空间卸载 DLL 时(即 DLL 的使用计数为零时),才会发生此调试事件。
DEBUG_EVENT 结构包含 UNLOAD_DLL_DEBUG_INFO 结构。 此结构指定 DLL 在卸载 DLL 的进程的地址空间中的基址。
通常,在收到此调试事件时,调试器将卸载与 DLL 关联的符号表。
进程退出时,系统会自动卸载进程的 DLL,但不会生成 UNLOAD_DLL_DEBUG_EVENT 调试事件。
RIP_EVENT
每当正在调试的进程在系统调试器无法控制的情况下终止时生成。
DEBUG_EVENT 结构包含 RIP_INFO 结构。 此结构指定错误和错误类型。