Debugger: Remove queued commands in GDB engine

Instead, always pipe them into the GDB process, even if the inferior
is not stopped. The original need to hold back commands until
the inferior is stopped seems to be long gone.

Change-Id: I234fdd067d78966c9896f1fa85a134900bf6dec7
Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
This commit is contained in:
hjk
2015-08-14 17:15:32 +02:00
parent ba2b210b83
commit cda1f94b18
3 changed files with 29 additions and 96 deletions

View File

@@ -238,6 +238,7 @@ GdbEngine::GdbEngine(const DebuggerRunParameters &startParameters)
m_commandsDoneCallback = 0; m_commandsDoneCallback = 0;
m_stackNeeded = false; m_stackNeeded = false;
m_terminalTrap = startParameters.useTerminal; m_terminalTrap = startParameters.useTerminal;
m_temporaryStopPending = false;
m_fullStartDone = false; m_fullStartDone = false;
m_systemDumpersLoaded = false; m_systemDumpersLoaded = false;
m_rerunPending = false; m_rerunPending = false;
@@ -831,7 +832,7 @@ void GdbEngine::interruptInferior()
return; return;
if (usesExecInterrupt()) { if (usesExecInterrupt()) {
postCommand("-exec-interrupt", Immediate); postCommand("-exec-interrupt");
} else { } else {
showStatusMessage(tr("Stop requested..."), 5000); showStatusMessage(tr("Stop requested..."), 5000);
showMessage(_("TRYING TO INTERRUPT INFERIOR")); showMessage(_("TRYING TO INTERRUPT INFERIOR"));
@@ -864,17 +865,6 @@ void GdbEngine::handleInterruptDeviceInferior(const QString &error)
m_signalOperation.clear(); m_signalOperation.clear();
} }
void GdbEngine::interruptInferiorTemporarily()
{
foreach (const DebuggerCommand &cmd, m_commandsToRunOnTemporaryBreak) {
if (cmd.flags & LosesChild) {
notifyInferiorIll();
return;
}
}
requestInterruptInferior();
}
void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0) void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0)
{ {
const qint64 pid = pid0.toLongLong(); const qint64 pid = pid0.toLongLong();
@@ -922,48 +912,24 @@ void GdbEngine::postCommand(const QByteArray &command, int flags,
if (!(cmd.flags & Discardable)) if (!(cmd.flags & Discardable))
++m_nonDiscardableCount; ++m_nonDiscardableCount;
// FIXME: clean up logic below if (cmd.flags & NeedsStop) {
if (cmd.flags & Immediate) { showMessage(_("RUNNING NEEDS-STOP COMMAND " + cmd.function));
// This should always be sent. if (state() == InferiorStopRequested) {
flushCommand(cmd); if (cmd.flags & LosesChild) {
} else if ((cmd.flags & NeedsStop) notifyInferiorIll();
|| !m_commandsToRunOnTemporaryBreak.isEmpty()) { return;
if (state() == InferiorStopOk || state() == InferiorUnrunnable
|| state() == InferiorSetupRequested || state() == EngineSetupOk
|| state() == InferiorShutdownRequested) {
// Can be safely sent now.
flushCommand(cmd);
} else {
// Queue the commands that we cannot send at once.
showMessage(_("QUEUING COMMAND " + cmd.function));
m_commandsToRunOnTemporaryBreak.append(cmd);
if (state() == InferiorStopRequested) {
if (cmd.flags & LosesChild)
notifyInferiorIll();
showMessage(_("CHILD ALREADY BEING INTERRUPTED. STILL HOPING."));
// Calling shutdown() here breaks all situations where two
// NeedsStop commands are issued in quick succession.
} else if (state() == InferiorRunOk) {
showStatusMessage(tr("Stopping temporarily"), 1000);
interruptInferiorTemporarily();
} else {
qDebug() << "ATTEMPTING TO QUEUE COMMAND "
<< cmd.function << "IN INAPPROPRIATE STATE" << state();
} }
showMessage(_("CHILD ALREADY BEING INTERRUPTED. STILL HOPING."));
// Calling shutdown() here breaks all situations where two
// NeedsStop commands are issued in quick succession.
} else if (!m_temporaryStopPending && state() == InferiorRunOk) {
showStatusMessage(tr("Stopping temporarily"), 1000);
m_temporaryStopPending = true;
requestInterruptInferior();
} }
} else if (!cmd.function.isEmpty()) {
flushCommand(cmd);
} }
}
void GdbEngine::flushQueuedCommands() flushCommand(cmd);
{
showStatusMessage(tr("Processing queued commands"), 1000);
while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
DebuggerCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
showMessage(_("RUNNING QUEUED COMMAND " + cmd.function));
flushCommand(cmd);
}
} }
void GdbEngine::flushCommand(const DebuggerCommand &cmd0) void GdbEngine::flushCommand(const DebuggerCommand &cmd0)
@@ -1213,13 +1179,6 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response)
<< "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests); << "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
} }
// Commands were queued, but we were in RunningRequested state, so the interrupt
// was postponed.
// This is done after the command callbacks so the running-requesting commands
// can assert on the right state.
if (state() == InferiorRunOk && !m_commandsToRunOnTemporaryBreak.isEmpty())
interruptInferiorTemporarily();
// Continue only if there are no commands wire anymore, so this will // Continue only if there are no commands wire anymore, so this will
// be fully synchronous. // be fully synchronous.
// This is somewhat inefficient, as it makes the last command synchronous. // This is somewhat inefficient, as it makes the last command synchronous.
@@ -1403,6 +1362,14 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
return; return;
} }
if (m_temporaryStopPending) {
showMessage(_("INTERNAL CONTINUE AFTER TEMPORARY STOP"), LogMisc);
m_temporaryStopPending = false;
notifyInferiorStopOk();
continueInferiorInternal();
return;
}
bool gotoHandleStop1 = true; bool gotoHandleStop1 = true;
if (!m_fullStartDone) { if (!m_fullStartDone) {
m_fullStartDone = true; m_fullStartDone = true;
@@ -1452,19 +1419,6 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
&& function != "qt_v4TriggeredBreakpointHook") && function != "qt_v4TriggeredBreakpointHook")
gotoLocation(Location(fullName, lineNumber)); gotoLocation(Location(fullName, lineNumber));
if (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
CHECK_STATE(InferiorStopRequested);
notifyInferiorStopOk();
flushQueuedCommands();
if (state() == InferiorStopOk) {
QTC_CHECK(m_commandsDoneCallback == 0);
m_commandsDoneCallback = &GdbEngine::autoContinueInferior;
} else {
CHECK_STATE(InferiorShutdownRequested);
}
return;
}
if (state() == InferiorRunOk) { if (state() == InferiorRunOk) {
// Stop triggered by a breakpoint or otherwise not directly // Stop triggered by a breakpoint or otherwise not directly
// initiated by the user. // initiated by the user.
@@ -1785,8 +1739,6 @@ void GdbEngine::handleExecuteContinue(const DebuggerResponse &response)
notifyInferiorRunFailed(); notifyInferiorRunFailed();
if (isDying()) if (isDying())
return; return;
if (!m_commandsToRunOnTemporaryBreak.isEmpty())
flushQueuedCommands();
CHECK_STATE(InferiorStopOk); CHECK_STATE(InferiorStopOk);
showStatusMessage(tr("Stopped."), 5000); showStatusMessage(tr("Stopped."), 5000);
reloadStack(); reloadStack();
@@ -1873,7 +1825,6 @@ QString GdbEngine::cleanupFullName(const QString &fileName)
void GdbEngine::shutdownInferior() void GdbEngine::shutdownInferior()
{ {
CHECK_STATE(InferiorShutdownRequested); CHECK_STATE(InferiorShutdownRequested);
m_commandsToRunOnTemporaryBreak.clear();
switch (runParameters().closeMode) { switch (runParameters().closeMode) {
case KillAtClose: case KillAtClose:
case KillAndExitMonitorAtClose: case KillAndExitMonitorAtClose:
@@ -2109,8 +2060,6 @@ void GdbEngine::handleExecuteStep(const DebuggerResponse &response)
notifyInferiorRunFailed(); notifyInferiorRunFailed();
if (isDying()) if (isDying())
return; return;
if (!m_commandsToRunOnTemporaryBreak.isEmpty())
flushQueuedCommands();
executeStepI(); // Fall back to instruction-wise stepping. executeStepI(); // Fall back to instruction-wise stepping.
} else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) { } else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) {
showExecutionError(QString::fromLocal8Bit(msg)); showExecutionError(QString::fromLocal8Bit(msg));
@@ -2185,8 +2134,6 @@ void GdbEngine::handleExecuteNext(const DebuggerResponse &response)
QByteArray msg = response.data["msg"].data(); QByteArray msg = response.data["msg"].data();
if (msg.startsWith("Cannot find bounds of current function") if (msg.startsWith("Cannot find bounds of current function")
|| msg.contains("Error accessing memory address ")) { || msg.contains("Error accessing memory address ")) {
if (!m_commandsToRunOnTemporaryBreak.isEmpty())
flushQueuedCommands();
notifyInferiorRunFailed(); notifyInferiorRunFailed();
if (!isDying()) if (!isDying())
executeNextI(); // Fall back to instruction-wise stepping. executeNextI(); // Fall back to instruction-wise stepping.
@@ -4265,7 +4212,7 @@ void GdbEngine::startGdb(const QStringList &args)
const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit() const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit()
+ "/data-directory/python"; + "/data-directory/python";
const GdbCommandFlags flags = ConsoleCommand | Immediate; const GdbCommandFlags flags = ConsoleCommand;
postCommand("python sys.path.insert(1, '" + dumperSourcePath + "')", flags); postCommand("python sys.path.insert(1, '" + dumperSourcePath + "')", flags);
postCommand("python sys.path.append('" + uninstalledData + "')", flags); postCommand("python sys.path.append('" + uninstalledData + "')", flags);
postCommand("python from gdbbridge import *", flags); postCommand("python from gdbbridge import *", flags);
@@ -4377,15 +4324,8 @@ void GdbEngine::resetInferior()
QByteArray commands = globalMacroExpander()->expand(runParameters().commandsForReset); QByteArray commands = globalMacroExpander()->expand(runParameters().commandsForReset);
foreach (QByteArray command, commands.split('\n')) { foreach (QByteArray command, commands.split('\n')) {
command = command.trimmed(); command = command.trimmed();
if (!command.isEmpty()) { if (!command.isEmpty())
if (state() == InferiorStopOk) { postCommand(command, ConsoleCommand|NeedsStop);
postCommand(command, ConsoleCommand|Immediate);
} else {
DebuggerCommand cmd(command);
cmd.flags = ConsoleCommand;
m_commandsToRunOnTemporaryBreak.append(cmd);
}
}
} }
} }
m_rerunPending = true; m_rerunPending = true;

View File

@@ -168,8 +168,6 @@ private: ////////// Gdb Command Management //////////
LosesChild = 64, LosesChild = 64,
// Trigger breakpoint model rebuild when no such commands are pending anymore. // Trigger breakpoint model rebuild when no such commands are pending anymore.
RebuildBreakpointModel = 128, RebuildBreakpointModel = 128,
// This command needs to be send immediately.
Immediate = 256,
// This is a command that needs to be wrapped into -interpreter-exec console // This is a command that needs to be wrapped into -interpreter-exec console
ConsoleCommand = 512, ConsoleCommand = 512,
// This is the UpdateLocals commannd during which we ignore notifications // This is the UpdateLocals commannd during which we ignore notifications
@@ -188,7 +186,6 @@ protected:
int flags = NoFlags, int flags = NoFlags,
DebuggerCommand::Callback callback = DebuggerCommand::Callback()); DebuggerCommand::Callback callback = DebuggerCommand::Callback());
private: private:
void flushQueuedCommands();
Q_SLOT void commandTimeout(); Q_SLOT void commandTimeout();
void setTokenBarrier(); void setTokenBarrier();
@@ -214,7 +211,6 @@ private:
// This function is called after all previous responses have been received. // This function is called after all previous responses have been received.
CommandsDoneCallback m_commandsDoneCallback; CommandsDoneCallback m_commandsDoneCallback;
QList<DebuggerCommand> m_commandsToRunOnTemporaryBreak;
bool m_rerunPending; bool m_rerunPending;
private: ////////// Gdb Output, State & Capability Handling ////////// private: ////////// Gdb Output, State & Capability Handling //////////
@@ -259,7 +255,6 @@ private: ////////// Inferior Management //////////
void continueInferior(); void continueInferior();
void interruptInferior(); void interruptInferior();
virtual void interruptInferior2() {} virtual void interruptInferior2() {}
void interruptInferiorTemporarily();
void executeRunToLine(const ContextData &data); void executeRunToLine(const ContextData &data);
void executeRunToFunction(const QString &functionName); void executeRunToFunction(const QString &functionName);
@@ -447,7 +442,7 @@ protected:
QString m_lastMissingDebugInfo; QString m_lastMissingDebugInfo;
BreakpointResponseId m_qFatalBreakpointResponseId; BreakpointResponseId m_qFatalBreakpointResponseId;
bool m_terminalTrap; bool m_terminalTrap;
bool m_temporaryStopPending;
bool usesExecInterrupt() const; bool usesExecInterrupt() const;
QHash<int, QByteArray> m_scheduledTestResponses; QHash<int, QByteArray> m_scheduledTestResponses;

View File

@@ -401,7 +401,6 @@ void GdbRemoteServerEngine::handleSetNtoExecutable(const DebuggerResponse &respo
QString msg = QString::fromLocal8Bit(response.data["msg"].data()); QString msg = QString::fromLocal8Bit(response.data["msg"].data());
notifyInferiorSetupFailed(msg); notifyInferiorSetupFailed(msg);
} }
} }
void GdbRemoteServerEngine::runEngine() void GdbRemoteServerEngine::runEngine()
@@ -435,8 +434,7 @@ void GdbRemoteServerEngine::interruptInferior2()
{ {
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state()); QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state());
if (boolSetting(TargetAsync)) { if (boolSetting(TargetAsync)) {
postCommand("-exec-interrupt", GdbEngine::Immediate, postCommand("-exec-interrupt", NoFlags, CB(handleInterruptInferior));
CB(handleInterruptInferior));
} else if (m_isQnxGdb && HostOsInfo::isWindowsHost()) { } else if (m_isQnxGdb && HostOsInfo::isWindowsHost()) {
m_gdbProc.interrupt(); m_gdbProc.interrupt();
} else { } else {