Why Address Breakpoints Are Disabled On Restart

If you set a [native] breakpoint by address, then restart your debugger session the breakpoint will be disabled, unlike most other breakpoint types. This was a deliberate change (in VS2003 or VS2005, I forget which), and here is why.

Address breakpoints are not often used if you have matching source and symbols, unless you are setting a breakpoint on an address within a line of source (more common in optimized code as it can be the only place to see variable values). They are considered an "advanced user" kind of feature. (Arguable the native debugger itself is a somewhat-advanced-user feature).

So why do address breakpoints get disabled on a restart? Because the code at their address may well not be the same as it was the last time the debugger was run. But worse than that, the address may no longer be at the start of an instruction boundary. The x86 (and x64) uses instructions of all byte lengths, so there is no heuristic to reliably determine whether an arbitrary address is the start of an instruction or not. (You can witness this heuristic by paging up in the disassembly window: this uses the infamous "backwards diassembler" which makes best efforts but still gets it wrong regularly).

The debugger uses the int 3 opcode (a byte of CC) for breakpoints, and if that gets inserted in the middle of an instruction then very weird things will happen. If you are lucky then it will crash or give an invalid instruction exception, but if you are unlucky then the instruction will execute wrongly (e.g. writing to the wrong offset in a structure) and you will get a weird problem much later on. Additionally you, the user, can not tell what happened because the debugger goes through some effort to never show you the int 3 (or the 0xCC opcode) in the disassembly or memory window: it will always show you what was there before the breakpoint was inserted.

After debugging customer issues (both the support folks and debugger team members were involved and these issues were never quick to figure out) the decision was made to disable address breakpoints on restart. This forces the user to think, at least a little, about that address before re-enabling it manually.

Some may point out that on ARM and IA64 <ug> architectures it is possible to determine if an address is at the start of an instruction because they are all the same size: while that is true, it does not help if that address that used to be code is now data (e.g. a jump thunk dword).

[Its been five years since I was on the debugger team but following some internal mail I thought I would revisit this issue so I don't have to explain this ever again]

Skip to main content