Intel processor architectures provide debug facilities for use in debugging code and monitoring system behaviors. Such a debug support is accessed using debug registers (DR0 through DR7), and handled by a dedicated exception routine.
Debug registers (Dr0~DR3) hold the addresses of memory and I/O locations called breakpoints. Breakpoints are user selected locations in a program, a data-storage area in memory, or specific I/O ports. They are set where a programmer or system designer wishes to halt execution of a program and examine the state of the processor by invoking debugger software. A debug exception (#DB, vector = 1) is generated when a memory or I/O access is made to a breakpoint address.
A typical debugger basically is able to set two types of breakpoints, one is software breakpoint, the other is hardware breakpoint. The former uses "INT 3" to trigger a software interrupt/trap. This technology requires code modification, the system software must insert an "INT 3" instruction at the address where the breakpoint is set by user. When such an instruction is executed, the CPU control will be transferred to the exception handler (#BP vector = 3), then this procedure will invoke a debugger if it is present. The latter approach configures processor debug registers (DRx) to set hardware breakpoints. Since there are only 4 debug registers that can be controlled to set breakpoints, the max number of hardware breakpoints is 4 simultaneously. However, generally speaking, the operating system stores all the debug registers at thread context structure, when thread switches, those configured debug registers will be restored, so multiple debugging sessions with 4 hardware breakpoints each can exist at the same time.
Note that one of interesting things is that when DR7.GD (Debug control register, general detect enable flag) is set, the #DB also occurs when a program attempts to write access any of Dr0~Dr7, This is a protection feature that guarantees full control over the debug registers. However, it can be bypassed by loading a new IDT table (LIDT) with a new #DB handler.
In a Virtualization Technology environment (VT-x), a hypervisor can use those debug registers to monitor individual guest IO or memory addresses without invasion to guest system software.
VT-x provides a VMCS (Virtual Machine Control Structure) Exception Bitmap that contains 32-bit fields for exceptions corresponding to the vector 0~31 in guest IDT table. When a bit is set, the corresponding guest exception will induce a VMexit, and the control is transferred to the VMexit handler in hypervisor, rather than being handled by a corresponding handler in guest IDT. So, when the bit 1 (#DB) of VMCS Exception Bitmap is set, any guest #DB exception causes a VM exit. Because a Hypervisor has full control of guest OS resources, it can also configure guest debug registers to set monitoring on individual guest IO or memory addresses in this way.
We can use this idea to monitoring guest OS thread scheduling events.