forked from qt-creator/qt-creator
Debugger: add callback for interrupt inferior
This allows to run any command without knowing the current state of the debugger engine. Replacing the old special stop mode for any command that may be triggered while the engine is running, with a check in runCommand. If the engine is running while a command is triggered a callback is added to the interrupt that will execute the command as soon as the engine is accessible again. Change-Id: I8bb1914b92da2b03e76c2c2ec8293d13041c72fd Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -151,35 +151,6 @@ namespace Internal {
|
||||
|
||||
static const char localsPrefixC[] = "local.";
|
||||
|
||||
struct MemoryViewCookie
|
||||
{
|
||||
explicit MemoryViewCookie(MemoryAgent *a = nullptr, quint64 addr = 0, quint64 l = 0)
|
||||
: agent(a), address(addr), length(l)
|
||||
{}
|
||||
|
||||
MemoryAgent *agent;
|
||||
quint64 address;
|
||||
quint64 length;
|
||||
};
|
||||
|
||||
struct MemoryChangeCookie
|
||||
{
|
||||
explicit MemoryChangeCookie(quint64 addr = 0, const QByteArray &d = QByteArray()) :
|
||||
address(addr), data(d) {}
|
||||
|
||||
quint64 address;
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
Q_DECLARE_METATYPE(Debugger::Internal::MemoryViewCookie)
|
||||
Q_DECLARE_METATYPE(Debugger::Internal::MemoryChangeCookie)
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
// Base data structure for command queue entries with callback
|
||||
class CdbCommand
|
||||
{
|
||||
@@ -251,11 +222,10 @@ void CdbEngine::init()
|
||||
{
|
||||
m_effectiveStartMode = NoStartMode;
|
||||
m_accessible = false;
|
||||
m_specialStopMode = NoSpecialStop;
|
||||
m_stopMode = NoStopRequested;
|
||||
m_nextCommandToken = 0;
|
||||
m_currentBuiltinResponseToken = -1;
|
||||
m_operateByInstructionPending = action(OperateByInstruction)->isChecked();
|
||||
m_operateByInstruction = true; // Default CDB setting
|
||||
m_operateByInstruction = true;
|
||||
m_hasDebuggee = false;
|
||||
m_sourceStepInto = false;
|
||||
m_watchPointX = m_watchPointY = 0;
|
||||
@@ -270,7 +240,7 @@ void CdbEngine::init()
|
||||
m_pendingBreakpointMap.clear();
|
||||
m_insertSubBreakpointMap.clear();
|
||||
m_pendingSubBreakpointMap.clear();
|
||||
m_customSpecialStopData.clear();
|
||||
m_interrupCallbacks.clear();
|
||||
m_symbolAddressCache.clear();
|
||||
m_coreStopReason.reset();
|
||||
|
||||
@@ -303,19 +273,8 @@ CdbEngine::~CdbEngine()
|
||||
|
||||
void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
|
||||
{
|
||||
// To be set next time session becomes accessible
|
||||
m_operateByInstructionPending = operateByInstruction;
|
||||
if (state() == InferiorStopOk)
|
||||
syncOperateByInstruction(operateByInstruction);
|
||||
}
|
||||
|
||||
void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
|
||||
{
|
||||
if (debug)
|
||||
qDebug("syncOperateByInstruction current: %d new %d", m_operateByInstruction, operateByInstruction);
|
||||
if (m_operateByInstruction == operateByInstruction)
|
||||
return;
|
||||
QTC_ASSERT(m_accessible, return);
|
||||
m_operateByInstruction = operateByInstruction;
|
||||
runCommand({QLatin1String(m_operateByInstruction ? "l-t" : "l+t"), NoFlags});
|
||||
runCommand({QLatin1String(m_operateByInstruction ? "l-s" : "l+s"), NoFlags});
|
||||
@@ -566,6 +525,7 @@ void CdbEngine::handleInitialSessionIdle()
|
||||
const DebuggerRunParameters &rp = runParameters();
|
||||
if (!rp.commandsAfterConnect.isEmpty())
|
||||
runCommand({rp.commandsAfterConnect, NoFlags});
|
||||
operateByInstructionTriggered(action(OperateByInstruction)->isChecked());
|
||||
// QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
|
||||
// (attemptBreakpointSynchronization() will be directly called then)
|
||||
attemptBreakpointSynchronization();
|
||||
@@ -864,14 +824,7 @@ void CdbEngine::interruptInferior()
|
||||
notifyInferiorRunOk();
|
||||
return;
|
||||
}
|
||||
doInterruptInferior(NoSpecialStop);
|
||||
}
|
||||
|
||||
void CdbEngine::doInterruptInferiorCustomSpecialStop(const QVariant &v)
|
||||
{
|
||||
if (m_specialStopMode == NoSpecialStop)
|
||||
doInterruptInferior(CustomSpecialStop);
|
||||
m_customSpecialStopData.push_back(v);
|
||||
doInterruptInferior();
|
||||
}
|
||||
|
||||
void CdbEngine::handleDoInterruptInferior(const QString &errorMessage)
|
||||
@@ -886,11 +839,20 @@ void CdbEngine::handleDoInterruptInferior(const QString &errorMessage)
|
||||
m_signalOperation.clear();
|
||||
}
|
||||
|
||||
void CdbEngine::doInterruptInferior(SpecialStopMode sm)
|
||||
void CdbEngine::doInterruptInferior(const InterruptCallback &callback)
|
||||
{
|
||||
showMessage(QString("Interrupting process %1...").arg(inferiorPid()), LogMisc);
|
||||
if (callback) {
|
||||
m_interrupCallbacks.push_back(callback);
|
||||
if (!m_initialSessionIdleHandled)
|
||||
return;
|
||||
if (m_stopMode == NoStopRequested)
|
||||
m_stopMode = Callback;
|
||||
} else {
|
||||
m_stopMode = Interrupt;
|
||||
}
|
||||
|
||||
QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed(); return;);
|
||||
showMessage(QString("Interrupting process %1...").arg(inferiorPid()), LogMisc);
|
||||
QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed(); return);
|
||||
if (DebuggerRunTool *rt = runTool()) {
|
||||
IDevice::ConstPtr device = rt->device();
|
||||
if (!device)
|
||||
@@ -898,7 +860,6 @@ void CdbEngine::doInterruptInferior(SpecialStopMode sm)
|
||||
if (device)
|
||||
m_signalOperation = device->signalOperation();
|
||||
}
|
||||
m_specialStopMode = sm;
|
||||
QTC_ASSERT(m_signalOperation, notifyInferiorStopFailed(); return;);
|
||||
connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
|
||||
this, &CdbEngine::handleDoInterruptInferior);
|
||||
@@ -1064,9 +1025,12 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
|
||||
{
|
||||
QString cmd = dbgCmd.function + dbgCmd.argsToString();
|
||||
if (!m_accessible) {
|
||||
const QString msg = QString("Attempt to issue command \"%1\" to non-accessible session (%2)")
|
||||
doInterruptInferior([this, dbgCmd](){
|
||||
runCommand(dbgCmd);
|
||||
});
|
||||
const QString msg = QString("Attempt to issue command \"%1\" to non-accessible session (%2)... interrupting")
|
||||
.arg(cmd, stateName(state()));
|
||||
showMessage(msg, LogError);
|
||||
showMessage(msg, LogMisc);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1492,34 +1456,26 @@ void CdbEngine::handleResolveSymbolHelper(const QList<quint64> &addresses, Disas
|
||||
}
|
||||
}
|
||||
|
||||
void CdbEngine::fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length)
|
||||
void CdbEngine::fetchMemory(MemoryAgent *agent, quint64 address, quint64 length)
|
||||
{
|
||||
if (debug)
|
||||
qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, addr);
|
||||
const MemoryViewCookie cookie(agent, addr, length);
|
||||
if (m_accessible)
|
||||
postFetchMemory(cookie);
|
||||
else
|
||||
doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
|
||||
}
|
||||
|
||||
void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
|
||||
{
|
||||
qDebug("CdbEngine::fetchMemory %llu bytes from 0x%llx", length, address);
|
||||
DebuggerCommand cmd("memory", ExtensionCommand);
|
||||
QString args;
|
||||
StringInputStream str(args);
|
||||
str << cookie.address << ' ' << cookie.length;
|
||||
str << address << ' ' << length;
|
||||
cmd.args = args;
|
||||
cmd.callback = [this, cookie](const DebuggerResponse &response) {
|
||||
if (!cookie.agent)
|
||||
cmd.callback = [this, agent = QPointer<MemoryAgent>(agent), address, length]
|
||||
(const DebuggerResponse &response) {
|
||||
if (!agent)
|
||||
return;
|
||||
if (response.resultClass == ResultDone) {
|
||||
const QByteArray data = QByteArray::fromHex(response.data.data().toUtf8());
|
||||
if (unsigned(data.size()) == cookie.length)
|
||||
cookie.agent->addData(cookie.address, data);
|
||||
if (unsigned(data.size()) == length)
|
||||
agent->addData(address, data);
|
||||
} else {
|
||||
showMessage(response.data["msg"].data(), LogWarning);
|
||||
cookie.agent->addData(cookie.address, QByteArray(int(cookie.length), char()));
|
||||
agent->addData(address, QByteArray(int(length), char()));
|
||||
}
|
||||
};
|
||||
runCommand(cmd);
|
||||
@@ -1528,12 +1484,7 @@ void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
|
||||
void CdbEngine::changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data)
|
||||
{
|
||||
QTC_ASSERT(!data.isEmpty(), return);
|
||||
if (!m_accessible) {
|
||||
const MemoryChangeCookie cookie(addr, data);
|
||||
doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
|
||||
} else {
|
||||
runCommand({cdbWriteMemoryCommand(addr, data), NoFlags});
|
||||
}
|
||||
runCommand({cdbWriteMemoryCommand(addr, data), NoFlags});
|
||||
}
|
||||
|
||||
void CdbEngine::reloadModules()
|
||||
@@ -1820,6 +1771,8 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
|
||||
return rc;
|
||||
}
|
||||
if (reason == "exception") {
|
||||
if (m_stopMode == Callback)
|
||||
rc |= StopIgnoreContinue;
|
||||
WinException exception;
|
||||
exception.fromGdbMI(stopReason);
|
||||
QString description = exception.toString();
|
||||
@@ -1862,6 +1815,7 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT
|
||||
ThreadId forcedThreadId;
|
||||
const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage,
|
||||
conditionalBreakPointTriggered);
|
||||
m_stopMode = NoStopRequested;
|
||||
// Do the non-blocking log reporting
|
||||
if (stopFlags & StopReportLog)
|
||||
showMessage(message, LogMisc);
|
||||
@@ -2049,9 +2003,9 @@ void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
|
||||
return;
|
||||
|
||||
if (debug)
|
||||
qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s', special mode %d",
|
||||
qDebug("CdbEngine::handleSessionAccessible %dms in state '%s'/'%s'",
|
||||
elapsedLogTime(), cdbStatusName(cdbExState),
|
||||
qPrintable(stateName(state())), m_specialStopMode);
|
||||
qPrintable(stateName(state())));
|
||||
|
||||
switch (s) {
|
||||
case EngineShutdownRequested:
|
||||
@@ -2074,9 +2028,9 @@ void CdbEngine::handleSessionInaccessible(unsigned long cdbExState)
|
||||
return;
|
||||
|
||||
if (debug)
|
||||
qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s', special mode %d",
|
||||
qDebug("CdbEngine::handleSessionInaccessible %dms in state '%s', '%s'",
|
||||
elapsedLogTime(), cdbStatusName(cdbExState),
|
||||
qPrintable(stateName(state())), m_specialStopMode);
|
||||
qPrintable(stateName(state())));
|
||||
|
||||
switch (state()) {
|
||||
case EngineSetupRequested:
|
||||
@@ -2113,37 +2067,13 @@ void CdbEngine::handleSessionIdle(const QString &message)
|
||||
return;
|
||||
|
||||
if (debug)
|
||||
qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
|
||||
qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s'",
|
||||
elapsedLogTime(), qPrintable(message),
|
||||
qPrintable(stateName(state())), m_specialStopMode);
|
||||
qPrintable(stateName(state())));
|
||||
|
||||
// Switch source level debugging
|
||||
syncOperateByInstruction(m_operateByInstructionPending);
|
||||
|
||||
// Engine-special stop reasons: Breakpoints and setup
|
||||
const SpecialStopMode specialStopMode = m_specialStopMode;
|
||||
|
||||
m_specialStopMode = NoSpecialStop;
|
||||
|
||||
switch (specialStopMode) {
|
||||
case SpecialStopSynchronizeBreakpoints:
|
||||
if (debug)
|
||||
qDebug("attemptBreakpointSynchronization in special stop");
|
||||
attemptBreakpointSynchronization();
|
||||
doContinueInferior();
|
||||
return;
|
||||
case SpecialStopGetWidgetAt:
|
||||
postWidgetAtCommand();
|
||||
return;
|
||||
case CustomSpecialStop:
|
||||
foreach (const QVariant &data, m_customSpecialStopData)
|
||||
handleCustomSpecialStop(data);
|
||||
m_customSpecialStopData.clear();
|
||||
doContinueInferior();
|
||||
return;
|
||||
case NoSpecialStop:
|
||||
break;
|
||||
}
|
||||
for (const InterruptCallback &callback : m_interrupCallbacks)
|
||||
callback();
|
||||
m_interrupCallbacks.clear();
|
||||
|
||||
if (!m_initialSessionIdleHandled) { // Temporary stop at beginning
|
||||
handleInitialSessionIdle();
|
||||
@@ -2603,12 +2533,6 @@ void CdbEngine::attemptBreakpointSynchronization()
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
if (!m_accessible) {
|
||||
// No nested calls.
|
||||
if (m_specialStopMode != SpecialStopSynchronizeBreakpoints)
|
||||
doInterruptInferior(SpecialStopSynchronizeBreakpoints);
|
||||
return;
|
||||
}
|
||||
// Add/Change breakpoints and store pending ones in map, since
|
||||
// Breakhandler::setResponse() on pending breakpoints clears the pending flag.
|
||||
// handleBreakPoints will the complete that information and set it on the break handler.
|
||||
@@ -3076,44 +3000,11 @@ void CdbEngine::watchPoint(const QPoint &p)
|
||||
{
|
||||
m_watchPointX = p.x();
|
||||
m_watchPointY = p.y();
|
||||
switch (state()) {
|
||||
case InferiorStopOk:
|
||||
postWidgetAtCommand();
|
||||
break;
|
||||
case InferiorRunOk:
|
||||
// "Select Widget to Watch" from a running application is currently not
|
||||
// supported. It could be implemented via SpecialStopGetWidgetAt-mode,
|
||||
// but requires some work as not to confuse the engine by state-change notifications
|
||||
// emitted by the debuggee function call.
|
||||
showMessage(tr("\"Select Widget to Watch\": Please stop the application first."), LogWarning);
|
||||
break;
|
||||
default:
|
||||
showMessage(tr("\"Select Widget to Watch\": Not supported in state \"%1\".").
|
||||
arg(stateName(state())), LogWarning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CdbEngine::postWidgetAtCommand()
|
||||
{
|
||||
DebuggerCommand cmd("widgetat", ExtensionCommand);
|
||||
cmd.args = QString("%1 %2").arg(m_watchPointX, m_watchPointY);
|
||||
cmd.args = QString("%1 %2").arg(p.x(), p.y());
|
||||
runCommand(cmd);
|
||||
}
|
||||
|
||||
void CdbEngine::handleCustomSpecialStop(const QVariant &v)
|
||||
{
|
||||
if (v.canConvert<MemoryChangeCookie>()) {
|
||||
const MemoryChangeCookie changeData = qvariant_cast<MemoryChangeCookie>(v);
|
||||
runCommand({cdbWriteMemoryCommand(changeData.address, changeData.data), NoFlags});
|
||||
return;
|
||||
}
|
||||
if (v.canConvert<MemoryViewCookie>()) {
|
||||
postFetchMemory(qvariant_cast<MemoryViewCookie>(v));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
|
||||
Reference in New Issue
Block a user