Utils: Don't raise SIGSTOP in the process stub

Previously, the starting inferior was sent a SIGSTOP to avoid
progress before the debugger could attach.

However, these signals are then also visible in the debugger and
need to be ignored as part of the startup handling in Creator.

The waiting effect can be achieved less intrusively by waiting
on a pipe read between fork() and exec().

Task-number: QTCREATORBUG-25073
Task-number: QTCREATORBUG-25082
Task-number: QTCREATORBUG-25227
Change-Id: Ie70b9eb5ea865f85411c26b0dbf377a019fec8d5
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2021-02-25 12:38:44 +01:00
parent d37f6648f3
commit 89646aadce
7 changed files with 50 additions and 41 deletions

View File

@@ -904,14 +904,6 @@ class Dumper(DumperBase):
except: # Could have been deleted in the mean time. except: # Could have been deleted in the mean time.
pass pass
self.ignoreStops = 0
if platform.system() == 'Linux':
if self.startMode_ == DebuggerStartMode.AttachCore:
pass
else:
if self.useTerminal_:
self.ignoreStops = 1
if self.platform_: if self.platform_:
self.debugger.SetCurrentPlatform(self.platform_) self.debugger.SetCurrentPlatform(self.platform_)
# sysroot has to be set *after* the platform # sysroot has to be set *after* the platform
@@ -1450,9 +1442,6 @@ class Dumper(DumperBase):
if self.isInterrupting_: if self.isInterrupting_:
self.isInterrupting_ = False self.isInterrupting_ = False
self.reportState("inferiorstopok") self.reportState("inferiorstopok")
elif self.ignoreStops > 0:
self.ignoreStops -= 1
self.process.Continue()
else: else:
self.reportState("stopped") self.reportState("stopped")
else: else:

View File

@@ -611,6 +611,18 @@ bool ConsoleProcess::start()
return true; return true;
} }
void Utils::ConsoleProcess::kickoffProcess()
{
#ifdef Q_OS_WIN
// Not used.
#else
if (d->m_stubSocket && d->m_stubSocket->isWritable()) {
d->m_stubSocket->write("c", 1);
d->m_stubSocket->flush();
}
#endif
}
void ConsoleProcess::interruptProcess() void ConsoleProcess::interruptProcess()
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN

View File

@@ -90,6 +90,7 @@ public:
bool isRunning() const; // This reflects the state of the console+stub bool isRunning() const; // This reflects the state of the console+stub
qint64 applicationPID() const; qint64 applicationPID() const;
void kickoffProcess();
void interruptProcess(); void interruptProcess();
void killProcess(); void killProcess();
void killStub(); void killStub();

View File

@@ -57,6 +57,7 @@ extern char **environ;
static int qtcFd; static int qtcFd;
static char *sleepMsg; static char *sleepMsg;
static int chldPipe[2]; static int chldPipe[2];
static int blockingPipe[2];
static int isDebug; static int isDebug;
static volatile int isDetached; static volatile int isDetached;
static volatile int chldPid; static volatile int chldPid;
@@ -236,10 +237,21 @@ int main(int argc, char *argv[])
perror("Cannot create status pipe"); perror("Cannot create status pipe");
doExit(3); doExit(3);
} }
/* The debugged program is not supposed to inherit these handles. But we cannot /* The debugged program is not supposed to inherit these handles. But we cannot
* close the writing end before calling exec(). Just handle both ends the same way ... */ * close the writing end before calling exec(). Just handle both ends the same way ... */
fcntl(chldPipe[0], F_SETFD, FD_CLOEXEC); fcntl(chldPipe[0], F_SETFD, FD_CLOEXEC);
fcntl(chldPipe[1], F_SETFD, FD_CLOEXEC); fcntl(chldPipe[1], F_SETFD, FD_CLOEXEC);
if (isDebug) {
/* Create execution start notification pipe. The child waits on this until
the parent writes to it, triggered by an 'c' message from Creator */
if (pipe(blockingPipe)) {
perror("Cannot create blocking pipe");
doExit(3);
}
}
switch ((chldPid = fork())) { switch ((chldPid = fork())) {
case -1: case -1:
perror("Cannot fork child process"); perror("Cannot fork child process");
@@ -262,9 +274,15 @@ int main(int argc, char *argv[])
#ifdef __linux__ #ifdef __linux__
prctl(PR_SET_PTRACER, atoi(argv[ArgPid])); prctl(PR_SET_PTRACER, atoi(argv[ArgPid]));
#endif #endif
/* Stop the child to allow the debugger to attach */ /* Block to allow the debugger to attach */
if (isDebug) if (isDebug) {
kill(chldPid, SIGSTOP); char buf;
int res = read(blockingPipe[0], &buf, 1);
if (res < 0)
perror("Could not read from blocking pipe");
close(blockingPipe[0]);
close(blockingPipe[1]);
}
if (env) if (env)
environ = env; environ = env;
@@ -296,6 +314,7 @@ int main(int argc, char *argv[])
break; break;
} else { } else {
int i; int i;
char c = 'i';
for (i = 0; i < nbytes; ++i) { for (i = 0; i < nbytes; ++i) {
switch (buffer[i]) { switch (buffer[i]) {
case 'k': case 'k':
@@ -305,13 +324,12 @@ int main(int argc, char *argv[])
kill(chldPid, SIGKILL); kill(chldPid, SIGKILL);
} }
break; break;
case 'i': case 'c': {
if (chldPid > 0) { int res = write(blockingPipe[1], &c, 1);
int res = kill(chldPid, SIGINT); if (res < 0)
if (res) perror("Could not write to blocking pipe");
perror("Stub could not interrupt inferior");
}
break; break;
}
case 'd': case 'd':
isDetached = 1; isDetached = 1;
break; break;

View File

@@ -1147,26 +1147,6 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
return; return;
} }
// Ignore signals from the process stub.
const GdbMi frame = data["frame"];
if (terminal()
&& data["reason"].data() == "signal-received"
&& data["signal-name"].data() == "SIGSTOP")
{
const QString from = frame["from"].data();
const QString func = frame["func"].data();
if (from.endsWith("/ld-linux.so.2")
|| from.endsWith("/ld-linux-x86-64.so.2")
|| func == "clone"
|| func == "kill")
{
showMessage("INTERNAL CONTINUE AFTER SIGSTOP FROM STUB", LogMisc);
notifyInferiorSpontaneousStop();
continueInferiorInternal();
return;
}
}
if (!m_onStop.isEmpty()) { if (!m_onStop.isEmpty()) {
notifyInferiorStopOk(); notifyInferiorStopOk();
showMessage("HANDLING QUEUED COMMANDS AFTER TEMPORARY STOP", LogMisc); showMessage("HANDLING QUEUED COMMANDS AFTER TEMPORARY STOP", LogMisc);
@@ -1184,6 +1164,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
QString fullName; QString fullName;
QString function; QString function;
QString language; QString language;
const GdbMi frame = data["frame"];
if (frame.isValid()) { if (frame.isValid()) {
const GdbMi lineNumberG = frame["line"]; const GdbMi lineNumberG = frame["line"];
function = frame["function"].data(); // V4 protocol function = frame["function"].data(); // V4 protocol
@@ -4919,7 +4900,9 @@ void GdbEngine::handleStubAttached(const DebuggerResponse &response, qint64 main
notifyEngineRunAndInferiorStopOk(); notifyEngineRunAndInferiorStopOk();
continueInferiorInternal(); continueInferiorInternal();
} else { } else {
showMessage("INFERIOR ATTACHED AND RUNNING"); showMessage("INFERIOR ATTACHED");
QTC_ASSERT(terminal(), return);
terminal()->kickoffProcess();
//notifyEngineRunAndInferiorRunOk(); //notifyEngineRunAndInferiorRunOk();
// Wait for the upcoming *stopped and handle it there. // Wait for the upcoming *stopped and handle it there.
} }

View File

@@ -183,6 +183,11 @@ TerminalRunner::TerminalRunner(RunControl *runControl, const Runnable &stubRunna
this, [this] { reportDone(); }); this, [this] { reportDone(); });
} }
void TerminalRunner::kickoffProcess()
{
m_stubProc.kickoffProcess();
}
void TerminalRunner::interruptProcess() void TerminalRunner::interruptProcess()
{ {
m_stubProc.interruptProcess(); m_stubProc.interruptProcess();

View File

@@ -77,6 +77,7 @@ public:
qint64 applicationPid() const { return m_applicationPid; } qint64 applicationPid() const { return m_applicationPid; }
qint64 applicationMainThreadId() const { return m_applicationMainThreadId; } qint64 applicationMainThreadId() const { return m_applicationMainThreadId; }
void kickoffProcess();
void interruptProcess(); void interruptProcess();
void setRunAsRoot(bool on); void setRunAsRoot(bool on);