Skip to content

Commit 5bbe63e

Browse files
authored
fix: Target Process may crash or freezes on detaching process on windows (llvm#115712)
Fixes llvm#67825 Fixes llvm#89077 Fixes [RIDER-99436](https://youtrack.jetbrains.com/issue/RIDER-99436/Unity-Editor-will-be-crashed-when-detaching-LLDB-debugger-in-Rider), which is upstream issue of llvm#67825. This PR changes the timing of calling `DebugActiveProcessStop` to after calling `ContinueDebugEvent` for last debugger exception. I confirmed the crashing behavior is because we call `DebugActiveProcessStop` before `ContinueDebugEvent` for last debugger exception with https://github.com/anatawa12/debug-api-test.
1 parent 649e4bf commit 5bbe63e

File tree

4 files changed

+82
-18
lines changed

4 files changed

+82
-18
lines changed

lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,13 @@ void DebuggerThread::DebugLoop() {
239239
BOOL wait_result = WaitForDebugEvent(&dbe, INFINITE);
240240
if (wait_result) {
241241
DWORD continue_status = DBG_CONTINUE;
242+
bool shutting_down = m_is_shutting_down;
242243
switch (dbe.dwDebugEventCode) {
243244
default:
244245
llvm_unreachable("Unhandle debug event code!");
245246
case EXCEPTION_DEBUG_EVENT: {
246-
ExceptionResult status =
247-
HandleExceptionEvent(dbe.u.Exception, dbe.dwThreadId);
247+
ExceptionResult status = HandleExceptionEvent(
248+
dbe.u.Exception, dbe.dwThreadId, shutting_down);
248249

249250
if (status == ExceptionResult::MaskException)
250251
continue_status = DBG_CONTINUE;
@@ -292,6 +293,45 @@ void DebuggerThread::DebugLoop() {
292293

293294
::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId, continue_status);
294295

296+
// We have to DebugActiveProcessStop after ContinueDebugEvent, otherwise
297+
// the target process will crash
298+
if (shutting_down) {
299+
// A breakpoint that occurs while `m_pid_to_detach` is non-zero is a
300+
// magic exception that we use simply to wake up the DebuggerThread so
301+
// that we can close out the debug loop.
302+
if (m_pid_to_detach != 0 &&
303+
(dbe.u.Exception.ExceptionRecord.ExceptionCode ==
304+
EXCEPTION_BREAKPOINT ||
305+
dbe.u.Exception.ExceptionRecord.ExceptionCode ==
306+
STATUS_WX86_BREAKPOINT)) {
307+
LLDB_LOG(log,
308+
"Breakpoint exception is cue to detach from process {0:x}",
309+
m_pid_to_detach.load());
310+
311+
// detaching with leaving breakpoint exception event on the queue may
312+
// cause target process to crash so process events as possible since
313+
// target threads are running at this time, there is possibility to
314+
// have some breakpoint exception between last WaitForDebugEvent and
315+
// DebugActiveProcessStop but ignore for now.
316+
while (WaitForDebugEvent(&dbe, 0)) {
317+
continue_status = DBG_CONTINUE;
318+
if (dbe.dwDebugEventCode == EXCEPTION_DEBUG_EVENT &&
319+
!(dbe.u.Exception.ExceptionRecord.ExceptionCode ==
320+
EXCEPTION_BREAKPOINT ||
321+
dbe.u.Exception.ExceptionRecord.ExceptionCode ==
322+
STATUS_WX86_BREAKPOINT ||
323+
dbe.u.Exception.ExceptionRecord.ExceptionCode ==
324+
EXCEPTION_SINGLE_STEP))
325+
continue_status = DBG_EXCEPTION_NOT_HANDLED;
326+
::ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId,
327+
continue_status);
328+
}
329+
330+
::DebugActiveProcessStop(m_pid_to_detach);
331+
m_detached = true;
332+
}
333+
}
334+
295335
if (m_detached) {
296336
should_debug = false;
297337
}
@@ -310,25 +350,18 @@ void DebuggerThread::DebugLoop() {
310350

311351
ExceptionResult
312352
DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info,
313-
DWORD thread_id) {
353+
DWORD thread_id, bool shutting_down) {
314354
Log *log = GetLog(WindowsLog::Event | WindowsLog::Exception);
315-
if (m_is_shutting_down) {
316-
// A breakpoint that occurs while `m_pid_to_detach` is non-zero is a magic
317-
// exception that
318-
// we use simply to wake up the DebuggerThread so that we can close out the
319-
// debug loop.
320-
if (m_pid_to_detach != 0 &&
355+
if (shutting_down) {
356+
bool is_breakpoint =
321357
(info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT ||
322-
info.ExceptionRecord.ExceptionCode == STATUS_WX86_BREAKPOINT)) {
323-
LLDB_LOG(log, "Breakpoint exception is cue to detach from process {0:x}",
324-
m_pid_to_detach.load());
325-
::DebugActiveProcessStop(m_pid_to_detach);
326-
m_detached = true;
327-
}
358+
info.ExceptionRecord.ExceptionCode == STATUS_WX86_BREAKPOINT);
328359

329360
// Don't perform any blocking operations while we're shutting down. That
330361
// will cause TerminateProcess -> WaitForSingleObject to time out.
331-
return ExceptionResult::SendToApplication;
362+
// We should not send breakpoint exceptions to the application.
363+
return is_breakpoint ? ExceptionResult::MaskException
364+
: ExceptionResult::SendToApplication;
332365
}
333366

334367
bool first_chance = (info.dwFirstChance != 0);

lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
4646
void FreeProcessHandles();
4747
void DebugLoop();
4848
ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info,
49-
DWORD thread_id);
49+
DWORD thread_id, bool shutting_down);
5050
DWORD HandleCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO &info,
5151
DWORD thread_id);
5252
DWORD HandleCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO &info,

lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,38 @@ Status ProcessWindows::DoDetach(bool keep_stopped) {
169169
Log *log = GetLog(WindowsLog::Process);
170170
StateType private_state = GetPrivateState();
171171
if (private_state != eStateExited && private_state != eStateDetached) {
172+
if (!keep_stopped) {
173+
// if the thread is suspended by lldb, we have to resume threads before
174+
// detaching process. When we do after DetachProcess(), thread handles
175+
// become invalid so we do before detach.
176+
if (private_state == eStateStopped || private_state == eStateCrashed) {
177+
LLDB_LOG(log, "process {0} is in state {1}. Resuming for detach...",
178+
m_session_data->m_debugger->GetProcess().GetProcessId(),
179+
GetPrivateState());
180+
181+
LLDB_LOG(log, "resuming {0} threads for detach.",
182+
m_thread_list.GetSize());
183+
184+
bool failed = false;
185+
for (uint32_t i = 0; i < m_thread_list.GetSize(); ++i) {
186+
auto thread = std::static_pointer_cast<TargetThreadWindows>(
187+
m_thread_list.GetThreadAtIndex(i));
188+
Status result = thread->DoResume();
189+
if (result.Fail()) {
190+
failed = true;
191+
LLDB_LOG(log,
192+
"Trying to resume thread at index {0}, but failed with "
193+
"error {1}.",
194+
i, result);
195+
}
196+
}
197+
198+
if (failed) {
199+
error = Status::FromErrorString("Resuming Threads for Detach failed");
200+
}
201+
}
202+
}
203+
172204
error = DetachProcess();
173205
if (error.Success())
174206
SetPrivateState(eStateDetached);

lldb/test/API/commands/process/detach-resumes/TestDetachResumes.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
class DetachResumesTestCase(TestBase):
1313
NO_DEBUG_INFO_TESTCASE = True
1414

15-
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr89077")
1615
def test_detach_resumes(self):
1716
self.build()
1817
exe = self.getBuildArtifact()

0 commit comments

Comments
 (0)