~5Y ago I blogged about data breakpoints. A hefty bit of the discussion was devoted to persistence of hardware breakpoints across a thread switch: all four implementations mentioned assume that HW breakpoints persist across thread boundaries, and some rough testing showed that that was indeed the case back then. Alas, somewhere between Windows 7 and Windows 10 – this assumption broke. The naïve implementation via SetThreadContext now indeed sets the debug registers only in the context of a specific thread. I suspect a deep change in the OS scheduler broke it – possibly hardware tasks are used today where they previously weren’t, but I have no proof.
I’m aware of a single attempt to address this shortcoming and implement a truly cross-thread data breakpoint: a year ago my friend Meir Meshi published code that not only enumerates all existing threads and sets debug registers in their context, but also hooks thread creation (actually RtlCreateThread) via a coded assembly trampoline to make sure any thread created henceforth would respect the existing breakpoints. The code seemed to work marvelously for a while, and broke again in Windows 10 – where MS understandably recognizes patching of thread creation as an exploit, and bans it.
I set out to find a working alternative to hooking thread creation. Two immediate directions popped to mind: DLL_THREAD_ATTACH and the lesser known TLS callbacks. These are two documented hooks available to user mode upon thread creation, and seemed like a natural place to access a list of pre-set breakpoints and apply them to the context of new threads. Both these attempts fell short again, since it seems these hooks are called before the target thread is created (from the stack of a different process thread), and setting the debug registers in this context does not persist to the target thread.
Bottom line, it seems that as of 2016 you really have to be a debugger and handle CREATE_THREAD_DEBUG_EVENT to manage hardware breakpoints. I was recently told by John Robbins that the VS team are aware of this need but it just isn’t currently a priority (this might change if this UserVoice suggestion gets a bit more votes, though). Luckily, VS isn’t the only debugger in the MS universe – and in fact it integrates with a much stronger one.
A Real Solution
WinDBG (and siblings) had perfect hardware breakpoints (via ‘ba‘) since forever. It is a less known fact that VS integrates rather nicely with WinDBG, and for completeness I’ll rehash the integration steps here.
(1) Install WDK , and mark the integration with VS checkbox.
(2) Run the debugee without a debugger (Ctrl+F5) and attach to it via the newly added ‘Windows User Mode Debugger’ transport:
The debugging engine is now WinDBG. The debugging experience is noticeably different: the expression evaluator is different – e.g. the view at the watch window and what it agrees to process are changed, threads pane no longer has thread IDs (why??) etc. – but all in all the large majority of VS commands and keyboard shortcuts are nicely mapped to this new engine.
You should see a new ‘Debugger Immediate Window’ pane, that accepts command line inputs identical to that of WinDBG, and with a nice bonus of auto-completes and auto help:
While at a breakpoint, type a ba command at this window. For example, to break upon read (r) of any of the 4 bytes (r4) following the address 0x000000c1`3219f7f4 (the windbg engine likes a ` separator in the middle of x64 addresses), type:
ba r4 0x000000c1`3219f7f4
And enjoy your shiny new read breakpoints that works across existing threads and are inherited by newly created ones.