Tech Support > Microsoft Windows > Development Resources > Single Stepping Problem in Multithreaded Debugger
Single Stepping Problem in Multithreaded Debugger
Posted by Clint on September 29th, 2003


I have written a debugger which performs a live disassembly on the
debuggee process for Windows 2000. I use two threads for my debugger.
The first thread is the main debugger thread that loops on
WaitForDebugEvent and handles the event appropriately. When an
EXCEPTION_SINGLE_STEP occurs, I disassemble the instruction and set
the trap flag again so that it will repeat the disassembly
for the next instruction. The second thread polls for the Enter key,
which either sets or unsets the trap flag to the opposite state of
what it is currently set to. This way I can start and stop single
stepping when I want to. I have tested my debugger on NotePad.exe and
other windows applications.

The problem I am having is in starting and stopping single stepping.
When I get the context for the main debuggee thread with my second
thread, more than half the time the Eip value is the same
(0x77E12F5C). Single stepping will most often not resume when the Eip
value is 0x77E12F5C, which is the return from NtUserGetMessage in
USER32.dll. Nowhere in my disassembly output do I see the debuggee
thread reaching 0x77E12F5C. This is probably because Notepad is using
more than one thread, but I am using the same thread handle to get the
context in both of my debugger threads. I do not understand what
exactly is going on. I am new to Win32 programming and assembly. I
would greatly appreciate some advice from anyone who understands Win32
debuggers and USER32.dll.

If I run the debugger on Notepad and wait until Notepad is up and
running before I push enter, I only get 0x77E12F5C as the start and
stop address. My theory is that the thread is being suspended while
the window is not active. But I don't understand why, in my
disassembly, I don't see the debuggee app go into NtUserGetMessage's
address space. I am analyzing the context of the same thread handle.
The handle of this thread is set only during the create
process debug event.


--------------------------------------------------------------------------------
Inside my polling (second) thread:
--------------------------------------------------------------------------------

// bSingleStepping is initialized to FALSE

while(!bEscapeKeyPressed)
{
if(GetAsyncKeyState(VK_RETURN) & 0x8000)// && bWaiting)
{// Enter key is down
dwSuspCount = SuspendThread(hDbgThread);
if(WaitForSingleObject(hMutex, 5000L) != WAIT_OBJECT_0)
{// Important that this comes after suspension
std::cerr << "Mutex failed!" << std::endl;
}
CONTEXT ctx;
ZeroMemory(&ctx, sizeof(ctx));
ctx.ContextFlags = CONTEXT_CONTROL;
bSuccess = GetThreadContext(hDbgThread, &ctx);
CW32ErrChkr::CheckLastErr(bSuccess, "GetThreadContext", "");
DWORD dwActual = 0;
if(!bSingleStepping)
{// Start single stepping
ctx.EFlags = ctx.EFlags | 0x0100;
bSingleStepping = TRUE;
std::cout << "Start Addr: 0x" << std::hex << ctx.Eip << std::endl;
}// end if
else
{// Stop single stepping
if(ctx.EFlags & 0x0100)
{// Remove trap flag
ctx.EFlags ^= 0x0100;
}
bSingleStepping = FALSE;
std::cout << "Stop Addr: 0x" << std::hex << ctx.Eip << std::endl;
}// end else
bSuccess = SetThreadContext(hDbgThread, &ctx);
CW32ErrChkr::CheckLastErr(bSuccess, "SetThreadContext", "");
FlushInstructionCache(hDbgProcess, NULL, NULL);
dwStartAddr = ctx.Eip;
bSuccess = ReleaseMutex(hMutex);
CW32ErrChkr::CheckLastErr(bSuccess, "ReleaseMutex", "");
dwSuspCount = ResumeThread(hDbgThread);
if(dwSuspCount != 1)
{
CW32ErrChkr::CheckLastErr(0, "ResumeThread", "");
std::cerr << "Thread failed to resume." << std::endl;
}
Sleep(300);// should replace this with while loop that checks
// for key release
}//end if


Similar Posts