“There’s a ten second hang after doing this operation for no apparent reason. Can you investigate?”
Not an apparently hard problem, and one that was easy enough to reproduce. Okay, build in debug mode, attach the debugger and … the problem vanishes. Ah, one of those.
The good news is that a ten second pause is long enough to attach a debugger to the process and then look at what’s happening. The actual details of the bug aren’t relevant here. The mystery was why it didn’t occur in the debugger. A bit of tracking back uncovered something very akin to:
void Function()
{
std::auto_ptr<ProgressBar> progressBar;
if (Application::IsVisible() && !::IsDebuggerPresent())
{
progressBar.reset(new ProgressBar("Doing Work"));
}
DoWork();
}
Heisenbugs are annoying. Deliberately introduced ones doubly so. Unfortunately recompiling the set of modules this was in would have taken several hours. But there is an easy alternative. If we disassemble the code around the test then we find.
0:000> u @eip @eip + 30
testapp!Example::Function+0x40
00401060 e89bffffff call testapp!Application::IsVisible (00401000)
00401065 0fb6c0 movzx eax,al
00401068 85c0 test eax,eax
0040106a 747b je testapp!Example::Function+0xc7 (004010e7)
0040106c ff1500204000 call dword ptr [testapp!_imp__IsDebuggerPresent (00402000)]
00401072 85c0 test eax,eax
00401074 7571 jne testapp!Example::Function+0xc7 (004010e7)
00401076 6a01 push 1
00401078 e85d010000 call testapp!operator new (004011da)
The interesting instruction being the test eax,eax
at
0x00401072
. We want to pretend the debugger isn’t set in this
instance so we can change the return value from the
IsDebuggerPresent
function by adding a breakpoint with a script
attached to it.
0:000> bp testapp!Example::Function+0x52 "r eax = 0; g"
So when our breakpoint is hit, it sets the value of EAX
to 0
(i.e. false) and sends the execution on it’s way again.
With this set we can continue to debug in the usual way without having to carefully wait for our hang and attaching quickly, much better.