Why Data Breakpoints are Disabled On Restart

Data breakpoints are both incredibly useful when you need them and yet can cause extreme pain under some circumstances. For the latter reason they are always disabled on restart, and here is why.

First of what is a data breakpoint? Since at least VS2005 (maybe earlier, I forget) they are hardware breakpoints set using special CPU registers that allow the CPU to notify a debugger that the memory at that address is written to (or read but VS does not support that mode). They are a very limited resource (there are four addresses on x86) and have limits on their size (1,2 or 4 bytes) and alignment (the address alignment has to match the size). Those are the CPU-level limitations, but to a user-mode debugger on Windows there are additional caveats.

One problem is related to the one described in Why Address Breakpoints Are Disabled On Restart : they are set by address, and that address may not be valid in the next debug session. Data breakpoints are usually set on heap addresses, and they are almost never the same from one run to another. (If set on a global then the address will be constant, at least until you recompile or your DLL gets loaded at a different address to due a collision).

However a less obvious problem is what happens when kernel code writes to an address that has a CPU breakpoint set on it. Lets say you have one set and then call ReadFile with a buffer pointer that includes the address. At some point lets assume the kernel does a memcpy over the magic address: this will raise a kernel-level exception as the data bp fires. In due course this will unwind to a try/except that is somewhere higher in the kernel stack, which will eventually get back to user mode and cause ReadFile to return FALSE (as well as to only partially fill the read buffer). Note that a user-mode debugger has no visiblity into kernel-mode exceptions, so is none the wiser. So by having the address breakpoint get written to by kernel code, not only did the breakpoint not fire when it happened but the program actually executes differently (reporting a bogus read error) as a result.

What if the memory is overwritten by something in the kernel more complicated than memcpy, like say a DMA operation? Well nothing as far as I was ever able to work out. The CPU doesn’t see that as a write operation so the data breakpoint never fires even at the kernel level. The memory will be overwritten, but the debugger can’t possible tell you about it.

Historic note: ancient versions of the VS debugger used “emulated data breakpoints” which were a performance nightmare as they manually polled the memory locations. For this and other reasons (to do with the expression evaulator and general user confusion) they were removed.