@@ -238,23 +238,6 @@ bool WindowsNativeAdapter::Detach()
238238 // Wake up the debug thread if it's waiting
239239 m_debugCondition.notify_one ();
240240
241- // Remove all breakpoints before detaching
242- {
243- std::lock_guard<std::mutex> lock (m_breakpointsMutex);
244- for (auto & bp : m_breakpoints)
245- {
246- if (bp.isActive )
247- RemoveBreakpointInternal (bp.address );
248- }
249- m_breakpoints.clear ();
250- }
251-
252- if (!DebugActiveProcessStop (m_processId))
253- {
254- LogError (" Failed to detach from process: %d" , GetLastError ());
255- return false ;
256- }
257-
258241 if (m_debugThread.joinable ())
259242 m_debugThread.join ();
260243
@@ -563,7 +546,16 @@ void WindowsNativeAdapter::DebugLoop()
563546
564547 if (m_shouldStop)
565548 {
549+ RemoveAllBreakpoints ();
566550 ContinueDebugEvent (debugEvent.dwProcessId , debugEvent.dwThreadId , DBG_CONTINUE);
551+
552+ // DebugActiveProcessStop must be called from the same thread that started debugging
553+ if (!DebugActiveProcessStop (m_processId))
554+ {
555+ LogWarn (" DebugActiveProcessStop failed (error %d) -- killing target" , GetLastError ());
556+ TerminateProcess (m_processHandle, 1 );
557+ }
558+
567559 break ;
568560 }
569561 }
@@ -588,6 +580,19 @@ void WindowsNativeAdapter::DebugLoop()
588580 ContinueDebugEvent (debugEvent.dwProcessId , debugEvent.dwThreadId , continueStatus);
589581 }
590582
583+ // If we exited the loop due to m_shouldStop while the target was running (not stopped at a
584+ // breakpoint), we still need to detach. The stopped-at-breakpoint case is handled inside the loop.
585+ if (m_shouldStop && m_activelyDebugging)
586+ {
587+ RemoveAllBreakpoints ();
588+
589+ if (!DebugActiveProcessStop (m_processId))
590+ {
591+ LogWarn (" DebugActiveProcessStop failed (error %d) -- killing target" , GetLastError ());
592+ TerminateProcess (m_processHandle, 1 );
593+ }
594+ }
595+
591596 m_activelyDebugging = false ;
592597}
593598
@@ -1569,6 +1574,58 @@ bool WindowsNativeAdapter::RemoveBreakpoint(const ModuleNameAndOffset& breakpoin
15691574}
15701575
15711576
1577+ void WindowsNativeAdapter::RemoveAllBreakpoints ()
1578+ {
1579+ // Remove software breakpoints
1580+ {
1581+ std::lock_guard<std::mutex> lock (m_breakpointsMutex);
1582+ for (auto & bp : m_breakpoints)
1583+ {
1584+ if (bp.isActive )
1585+ RemoveBreakpointInternal (bp.address );
1586+ }
1587+ m_breakpoints.clear ();
1588+ }
1589+
1590+ // Remove hardware breakpoints from all threads
1591+ {
1592+ std::lock_guard<std::mutex> lock (m_hwBreakpointsMutex);
1593+ for (const auto & hwbp : m_hardwareBreakpoints)
1594+ {
1595+ if (hwbp.isActive )
1596+ {
1597+ for (auto & [tid, handle] : m_threads)
1598+ {
1599+ if (!handle)
1600+ continue ;
1601+ if (m_isTargetWow64)
1602+ {
1603+ WOW64_CONTEXT ctx {};
1604+ ctx.ContextFlags = WOW64_CONTEXT_DEBUG_REGISTERS;
1605+ if (Wow64GetThreadContext (handle, &ctx))
1606+ {
1607+ if (ClearHardwareBreakpointInContext (ctx, hwbp.drIndex ))
1608+ Wow64SetThreadContext (handle, &ctx);
1609+ }
1610+ }
1611+ else
1612+ {
1613+ CONTEXT ctx {};
1614+ ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
1615+ if (GetThreadContext (handle, &ctx))
1616+ {
1617+ if (ClearHardwareBreakpointInContext (ctx, hwbp.drIndex ))
1618+ SetThreadContext (handle, &ctx);
1619+ }
1620+ }
1621+ }
1622+ }
1623+ }
1624+ m_hardwareBreakpoints.clear ();
1625+ }
1626+ }
1627+
1628+
15721629bool WindowsNativeAdapter::RemoveBreakpointInternal (uint64_t address)
15731630{
15741631 // Find the breakpoint to get the original byte
0 commit comments