|
10 | 10 |
|
11 | 11 | #include "DebugServer2/Target/Process.h" |
12 | 12 | #include "DebugServer2/Core/BreakpointManager.h" |
| 13 | +#include "DebugServer2/Host/Platform.h" |
13 | 14 | #include "DebugServer2/Host/POSIX/PTrace.h" |
14 | 15 | #include "DebugServer2/Target/Thread.h" |
15 | 16 | #include "DebugServer2/Utils/Log.h" |
@@ -176,6 +177,75 @@ void Process::setSignalPass(int signo, bool set) { |
176 | 177 | _passthruSignals.erase(signo); |
177 | 178 | } |
178 | 179 | } |
| 180 | + |
| 181 | +// Used to interrupt calls to wait(2), waitpid(2), and waitid(2) that are |
| 182 | +// blocked waiting for inferior process events. Requried in the scenario where |
| 183 | +// the debug server's main thread is blocked waiting for events from an |
| 184 | +// inferior process whose threads are already in a stopped state. E.g.: |
| 185 | +// 1. All threads of the inferior process are in a stopped state. |
| 186 | +// 2. The main thread of the debug server is blocked in a waitpid() call |
| 187 | +// waiting for an event from the inferior process. |
| 188 | +// 2. The debug server process receives an asynchronous \x03 interrupt packet |
| 189 | +// from the debugger and attempts to interrupt the inferior by calling |
| 190 | +// kill(SIGSTOP). |
| 191 | +// 3. The kill(SIGSTOP) call has no effect on the inferior process because |
| 192 | +// every thread is already stopped. |
| 193 | +// 4. The call to waitpid() remains blocked indefinitely. |
| 194 | +// |
| 195 | +// In this situation, the debug server can use sendInterrupt in the following |
| 196 | +// way to interrupt the waitpid() call: |
| 197 | +// 1. In response to the interrupt packet from the debugger, the debug server |
| 198 | +// calls sendInterrupt() in addition to kill(SIGSTOP). This call forks a |
| 199 | +// short-lived child process and saves its pid. The child process |
| 200 | +// immediately exits, generating a thread exit event. |
| 201 | +// 2. The debug server's main thread now returns from waitpid(). It calls |
| 202 | +// checkInterrupt() with the results from waitpid(). The call compares the |
| 203 | +// pid returned by waitpid() with the pid stashed by sendInterrupt(), which |
| 204 | +// returns true if pid matches the one saved during the last call to |
| 205 | +// sendInterrupt(). |
| 206 | +// |
| 207 | +// NOTE: There can be only one pending interrupt at a time. Subsequent calls |
| 208 | +// to sendInterrupt() without corresponding calls to checkInterrupt() are |
| 209 | +// noops. |
| 210 | +ErrorCode Process::sendInterrupt() { |
| 211 | + // Mutex guards against concurrent calls to checkInterrupt and sendInterrupt. |
| 212 | + // It is required to atomically fork the interrupting process and memoize it's |
| 213 | + // pid in the _pid field. |
| 214 | + std::lock_guard<std::mutex> lock(_interruptState.mutex); |
| 215 | + if (_interruptState.pid > 0) // There is already a pending interrupt |
| 216 | + return kSuccess; |
| 217 | + |
| 218 | + const ProcessId pid = ::fork(); |
| 219 | + if (pid < 0) |
| 220 | + return Host::Platform::TranslateError(); |
| 221 | + |
| 222 | + if (pid == 0) { |
| 223 | + DS2LOG(Debug, "forked process %" PRI_PID " to interrupt waiter", |
| 224 | + ::getpid()); |
| 225 | + // Exiting the forked thread will wake the waiting parent process with |
| 226 | + // WIFEXITED status. |
| 227 | + exit(0); |
| 228 | + } |
| 229 | + |
| 230 | + _interruptState.pid = pid; |
| 231 | + return kSuccess; |
| 232 | +} |
| 233 | + |
| 234 | +bool Process::checkInterrupt(ThreadId tid, int waitStatus) { |
| 235 | + { |
| 236 | + std::lock_guard<std::mutex> lock(_interruptState.mutex); |
| 237 | + if (_interruptState.pid <= 0) // no interrupt |
| 238 | + return false; |
| 239 | + |
| 240 | + if (_interruptState.pid != tid || !WIFEXITED(waitStatus)) // tid does not match interrupt |
| 241 | + return false; |
| 242 | + |
| 243 | + _interruptState.pid = 0; // clear interrupt |
| 244 | + } |
| 245 | + |
| 246 | + DS2LOG(Debug, "received interrupt from process %" PRI_PID, tid); |
| 247 | + return true; |
| 248 | +} |
179 | 249 | } // namespace POSIX |
180 | 250 | } // namespace Target |
181 | 251 | } // namespace ds2 |
0 commit comments