forked from qt-creator/qt-creator
Debugger[CDB]: Reduce roundtrips when stepping.
Report threads and frames along with stopped notification. Change special logic for step into and switching threads on interrupt accordingly.
This commit is contained in:
@@ -929,9 +929,9 @@ unsigned CdbEngine::debuggerCapabilities() const
|
||||
|
||||
void CdbEngine::executeStep()
|
||||
{
|
||||
postCommand(QByteArray("t"), 0); // Step into-> t (trace)
|
||||
if (!m_operateByInstruction)
|
||||
m_sourceStepInto = true; // See explanation at handleStackTrace().
|
||||
postCommand(QByteArray("t"), 0); // Step into-> t (trace)
|
||||
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
|
||||
notifyInferiorRunRequested();
|
||||
}
|
||||
@@ -1090,6 +1090,15 @@ void CdbEngine::assignValueInDebugger(const WatchData *w, const QString &expr, c
|
||||
updateLocals();
|
||||
}
|
||||
|
||||
void CdbEngine::parseThreads(const GdbMi &data, int forceCurrentThreadId /* = -1 */)
|
||||
{
|
||||
int currentThreadId;
|
||||
Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
|
||||
threadsHandler()->setThreads(threads);
|
||||
threadsHandler()->setCurrentThreadId(forceCurrentThreadId >= 0 ?
|
||||
forceCurrentThreadId : currentThreadId);
|
||||
}
|
||||
|
||||
void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
|
||||
{
|
||||
if (debug)
|
||||
@@ -1097,10 +1106,7 @@ void CdbEngine::handleThreads(const CdbExtensionCommandPtr &reply)
|
||||
if (reply->success) {
|
||||
GdbMi data;
|
||||
data.fromString(reply->reply);
|
||||
int currentThreadId;
|
||||
Threads threads = ThreadsHandler::parseGdbmiThreads(data, ¤tThreadId);
|
||||
threadsHandler()->setThreads(threads);
|
||||
threadsHandler()->setCurrentThreadId(currentThreadId);
|
||||
parseThreads(data);
|
||||
// Continue sequence
|
||||
postCommandSequence(reply->commandSequence);
|
||||
} else {
|
||||
@@ -1572,13 +1578,11 @@ enum StopActionFlags
|
||||
StopInArtificialThread = 0x40
|
||||
};
|
||||
|
||||
unsigned CdbEngine::examineStopReason(const QByteArray &messageIn,
|
||||
unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
|
||||
QString *message,
|
||||
QString *exceptionBoxMessage)
|
||||
{
|
||||
// Report stop reason (GDBMI)
|
||||
GdbMi stopReason;
|
||||
stopReason.fromString(messageIn);
|
||||
if (debug)
|
||||
qDebug("%s", stopReason.toString(true, 4).constData());
|
||||
const QByteArray reason = stopReason.findChild("reason").data();
|
||||
@@ -1685,7 +1689,10 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
|
||||
// Further examine stop and report to user
|
||||
QString message;
|
||||
QString exceptionBoxMessage;
|
||||
const unsigned stopFlags = examineStopReason(messageBA, &message, &exceptionBoxMessage);
|
||||
GdbMi stopReason;
|
||||
stopReason.fromString(messageBA);
|
||||
int forcedThreadId = -1;
|
||||
const unsigned stopFlags = examineStopReason(stopReason, &message, &exceptionBoxMessage);
|
||||
// Do the non-blocking log reporting
|
||||
if (stopFlags & StopReportLog)
|
||||
showMessage(message, LogMisc);
|
||||
@@ -1707,17 +1714,37 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
|
||||
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop")
|
||||
notifyInferiorSpontaneousStop();
|
||||
}
|
||||
const bool sourceStepInto = m_sourceStepInto;
|
||||
m_sourceStepInto = false;
|
||||
// Start sequence to get all relevant data.
|
||||
if (stopFlags & StopInArtificialThread) {
|
||||
showMessage(tr("Switching to main thread..."), LogMisc);
|
||||
postCommand("~0 s", 0);
|
||||
forcedThreadId = 0;
|
||||
// Re-fetch stack again.
|
||||
postCommandSequence(CommandListStack);
|
||||
} else {
|
||||
const GdbMi stack = stopReason.findChild("stack");
|
||||
if (stack.isValid()) {
|
||||
if (parseStackTrace(stack, sourceStepInto) & ParseStackStepInto) {
|
||||
executeStep(); // Hit on a frame while step into, see parseStackTrace().
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
showMessage(QString::fromAscii(stopReason.findChild("stackerror").data()), LogError);
|
||||
}
|
||||
}
|
||||
unsigned sequence = CommandListStack|CommandListThreads;
|
||||
const GdbMi threads = stopReason.findChild("threads");
|
||||
if (threads.isValid()) {
|
||||
parseThreads(threads, forcedThreadId);
|
||||
} else {
|
||||
showMessage(QString::fromAscii(stopReason.findChild("threaderror").data()), LogError);
|
||||
}
|
||||
// Fire off remaining commands asynchronously
|
||||
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
|
||||
sequence |= CommandListRegisters;
|
||||
postCommandSequence(CommandListRegisters);
|
||||
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
|
||||
sequence |= CommandListModules;
|
||||
postCommandSequence(sequence);
|
||||
postCommandSequence(CommandListModules);
|
||||
}
|
||||
// After the sequence has been sent off and CDB is pondering the commands,
|
||||
// pop up a message box for exceptions.
|
||||
@@ -2176,13 +2203,8 @@ QString CdbEngine::normalizeFileName(const QString &f)
|
||||
|
||||
// Parse frame from GDBMI. Duplicate of the gdb code, but that
|
||||
// has more processing.
|
||||
static StackFrames parseFrames(const QByteArray &data)
|
||||
static StackFrames parseFrames(const GdbMi &gdbmi)
|
||||
{
|
||||
GdbMi gdbmi;
|
||||
gdbmi.fromString(data);
|
||||
if (!gdbmi.isValid())
|
||||
return StackFrames();
|
||||
|
||||
StackFrames rc;
|
||||
const int count = gdbmi.childCount();
|
||||
rc.reserve(count);
|
||||
@@ -2204,38 +2226,43 @@ static StackFrames parseFrames(const QByteArray &data)
|
||||
return rc;
|
||||
}
|
||||
|
||||
void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
|
||||
unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
|
||||
{
|
||||
// Parse frames, find current. Special handling for step into:
|
||||
// When stepping into on an actual function (source mode) by executing 't', an assembler
|
||||
// frame pointing at the jmp instruction is hit (noticeable by top function being
|
||||
// 'ILT+'). If that is the case, execute another 't' to step into the actual function. .
|
||||
// Note that executing 't 2' does not work since it steps 2 instructions on a non-call code line.
|
||||
const bool sourceStepInto = m_sourceStepInto;
|
||||
m_sourceStepInto = false;
|
||||
if (command->success) {
|
||||
int current = -1;
|
||||
StackFrames frames = parseFrames(command->reply);
|
||||
const int count = frames.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
const bool hasFile = !frames.at(i).file.isEmpty();
|
||||
// jmp-frame hit by step into, do another 't' and abort sequence.
|
||||
if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
|
||||
showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
|
||||
executeStep();
|
||||
return;
|
||||
}
|
||||
if (hasFile) {
|
||||
frames[i].file = QDir::cleanPath(normalizeFileName(frames.at(i).file));
|
||||
if (current == -1 && frames[i].usable)
|
||||
current = i;
|
||||
}
|
||||
int current = -1;
|
||||
StackFrames frames = parseFrames(data);
|
||||
const int count = frames.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
const bool hasFile = !frames.at(i).file.isEmpty();
|
||||
// jmp-frame hit by step into, do another 't' and abort sequence.
|
||||
if (!hasFile && i == 0 && sourceStepInto && frames.at(i).function.contains(QLatin1String("ILT+"))) {
|
||||
showMessage(QString::fromAscii("Step into: Call instruction hit, performing additional step..."), LogMisc);
|
||||
return ParseStackStepInto;
|
||||
}
|
||||
if (count && current == -1) // No usable frame, use assembly.
|
||||
current = 0;
|
||||
// Set
|
||||
stackHandler()->setFrames(frames);
|
||||
activateFrame(current);
|
||||
if (hasFile) {
|
||||
frames[i].file = QDir::cleanPath(normalizeFileName(frames.at(i).file));
|
||||
if (current == -1 && frames[i].usable)
|
||||
current = i;
|
||||
}
|
||||
}
|
||||
if (count && current == -1) // No usable frame, use assembly.
|
||||
current = 0;
|
||||
// Set
|
||||
stackHandler()->setFrames(frames);
|
||||
activateFrame(current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
|
||||
{
|
||||
if (command->success) {
|
||||
GdbMi data;
|
||||
data.fromString(command->reply);
|
||||
parseStackTrace(data, false);
|
||||
postCommandSequence(command->commandSequence);
|
||||
} else {
|
||||
showMessage(command->errorMessage, LogError);
|
||||
|
Reference in New Issue
Block a user