diff --git a/Headers/DebugServer2/Host/POSIX/PTrace.h b/Headers/DebugServer2/Host/POSIX/PTrace.h index 1e5085ec..a8786281 100644 --- a/Headers/DebugServer2/Host/POSIX/PTrace.h +++ b/Headers/DebugServer2/Host/POSIX/PTrace.h @@ -38,7 +38,7 @@ class PTrace { public: virtual ErrorCode attach(ProcessId pid); - virtual ErrorCode detach(ProcessId pid); + virtual ErrorCode detach(ProcessId pid, int signal = 0); public: virtual ErrorCode kill(ProcessThreadId const &ptid, int signal) = 0; diff --git a/Headers/DebugServer2/Target/POSIX/Process.h b/Headers/DebugServer2/Target/POSIX/Process.h index 4bed1e8c..182abdd5 100644 --- a/Headers/DebugServer2/Target/POSIX/Process.h +++ b/Headers/DebugServer2/Target/POSIX/Process.h @@ -39,7 +39,7 @@ class Process : public ds2::Target::ProcessBase { bool checkInterrupt(ThreadId tid, int waitStatus); public: - ErrorCode detach() override; + ErrorCode detach(bool stopped) override; ErrorCode interrupt() override; ErrorCode terminate() override; bool isAlive() const override; diff --git a/Headers/DebugServer2/Target/ProcessBase.h b/Headers/DebugServer2/Target/ProcessBase.h index c37b5ceb..9809c433 100644 --- a/Headers/DebugServer2/Target/ProcessBase.h +++ b/Headers/DebugServer2/Target/ProcessBase.h @@ -107,7 +107,7 @@ class ProcessBase { virtual void cleanup(); public: - virtual ErrorCode detach() = 0; + virtual ErrorCode detach(bool stopped) = 0; public: virtual ErrorCode suspend(); diff --git a/Headers/DebugServer2/Target/Windows/Process.h b/Headers/DebugServer2/Target/Windows/Process.h index abd7c921..81c77992 100644 --- a/Headers/DebugServer2/Target/Windows/Process.h +++ b/Headers/DebugServer2/Target/Windows/Process.h @@ -38,7 +38,7 @@ class Process : public ds2::Target::ProcessBase, ErrorCode writeDebugBreakCode(uint64_t address); public: - ErrorCode detach() override; + ErrorCode detach(bool stopped) override; ErrorCode interrupt() override; ErrorCode terminate() override; bool isAlive() const override; diff --git a/Sources/GDBRemote/DebugSessionImpl.cpp b/Sources/GDBRemote/DebugSessionImpl.cpp index 169e986f..66e2f2c6 100644 --- a/Sources/GDBRemote/DebugSessionImpl.cpp +++ b/Sources/GDBRemote/DebugSessionImpl.cpp @@ -1173,7 +1173,7 @@ ErrorCode DebugSessionImplBase::onDetach(Session &, ProcessId pid, CHK(_process->suspend()); } - return _process->detach(); + return _process->detach(stopped); } ErrorCode DebugSessionImplBase::onTerminate(Session &session, diff --git a/Sources/Host/POSIX/PTrace.cpp b/Sources/Host/POSIX/PTrace.cpp index 14775ded..8ded8e5a 100644 --- a/Sources/Host/POSIX/PTrace.cpp +++ b/Sources/Host/POSIX/PTrace.cpp @@ -76,13 +76,14 @@ ErrorCode PTrace::attach(ProcessId pid) { return kSuccess; } -ErrorCode PTrace::detach(ProcessId pid) { +ErrorCode PTrace::detach(ProcessId pid, int signal) { if (pid <= kAnyProcessId) return kErrorProcessNotFound; - DS2LOG(Debug, "detaching from pid %" PRIu64, (uint64_t)pid); + DS2LOG(Debug, "detaching from pid %" PRIu64 " with signal %d", (uint64_t)pid, + signal); - if (wrapPtrace(PTCMD(DETACH), pid, nullptr, nullptr) < 0) + if (wrapPtrace(PTCMD(DETACH), pid, nullptr, signal) < 0) return Platform::TranslateError(); return kSuccess; diff --git a/Sources/Target/POSIX/Process.cpp b/Sources/Target/POSIX/Process.cpp index beddde02..685b6ebb 100644 --- a/Sources/Target/POSIX/Process.cpp +++ b/Sources/Target/POSIX/Process.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -50,10 +51,33 @@ ErrorCode Process::initialize(ProcessId pid, uint32_t flags) { return kSuccess; } -ErrorCode Process::detach() { +ErrorCode Process::detach(bool stopped) { prepareForDetach(); - CHK(ptrace().detach(_pid)); + // PTRACE_DETACH resumes the tracee as it stops being traced, so a plain + // detach with no signal leaves it running. When the caller wants the + // target left stopped (LLDB's GDB-remote D1 detach-and-stay-stopped + // mode), SIGSTOP has to be delivered as part of the detach itself rather + // than relied on as a side effect of an earlier suspend, since suspending + // a thread beforehand only leaves a signal pending and does not by + // itself survive detaching. + int const signal = stopped ? SIGSTOP : 0; + + // ptrace() traces each thread of a multithreaded process as its own + // tracee. Threads created after the initial attach show up via + // PTRACE_EVENT_CLONE rather than a fresh PTRACE_ATTACH, but they still + // need their own PTRACE_DETACH. Detaching only the main pid would leave + // every other thread parked in its last ptrace-stop forever, so this + // best-effort detaches the rest of the threads first. + std::vector tids; + getThreadIds(tids); + for (ThreadId tid : tids) { + if (tid != _pid) { + ptrace().detach(tid, signal); + } + } + + CHK(ptrace().detach(_pid, signal)); cleanup(); _flags &= ~kFlagAttachedProcess; diff --git a/Sources/Target/Windows/Process.cpp b/Sources/Target/Windows/Process.cpp index 265c053f..7924ba7e 100644 --- a/Sources/Target/Windows/Process.cpp +++ b/Sources/Target/Windows/Process.cpp @@ -72,7 +72,7 @@ Process::~Process() { // exit, so we detach at this point. This is required because otherwise the // debugged winphone process might stay alive in an unclosable state. If // detached, the winphone process dies gracefully. - detach(); + detach(false); ::CloseHandle(_handle); } @@ -125,9 +125,14 @@ Target::Process *Process::Attach(ProcessId pid) { return process.release(); } -ErrorCode Process::detach() { +ErrorCode Process::detach(bool stopped) { prepareForDetach(); + // DebugActiveProcessStop has no equivalent of POSIX's detach-and-leave- + // stopped: the debuggee always resumes once debugging is stopped, so + // there is nothing here to do differently when stopped is requested. + (void)stopped; + BOOL result = DebugActiveProcessStop(_pid); if (!result) { return Platform::TranslateError();