diff --git a/share/qtcreator/dumper/bridge.py b/share/qtcreator/dumper/bridge.py index fe23f459528..2de5cdbf986 100644 --- a/share/qtcreator/dumper/bridge.py +++ b/share/qtcreator/dumper/bridge.py @@ -422,11 +422,34 @@ try: #warn("LOADING LLDB") + # Data members SimpleValueCode, \ StructCode, \ PointerCode \ = range(3) + # Breakpoints. Keep synchronized with BreakpointType in breakpoint.h + UnknownType = 0 + BreakpointByFileAndLine = 1 + BreakpointByFunction = 2 + BreakpointByAddress = 3 + BreakpointAtThrow = 4 + BreakpointAtCatch = 5 + BreakpointAtMain = 6 + BreakpointAtFork = 7 + BreakpointAtExec = 8 + BreakpointAtSysCall = 10 + WatchpointAtAddress = 11 + WatchpointAtExpression = 12 + BreakpointOnQmlSignalEmit = 13 + BreakpointAtJavaScriptThrow = 14 + + + import json + + def dumpJson(stuff): + warn("%s" % json.dumps(stuff, sort_keys=True, indent=4, separators=(',', ': '))) + def registerCommand(name, func): pass @@ -578,8 +601,133 @@ try: return result + def breakpoint_function_wrapper(baton, process, frame, bp_loc): + result = "*stopped" + result += ",line=\"%s\"" % frame.line_entry.line + result += ",file=\"%s\"" % frame.line_entry.file + warn("WRAPPER: %s " %result) + return result + + def initLldb(): + pass + + def dumpBreakpoint(bp, modelId): + cond = bp.GetCondition() + result = "{lldbid=\"%s\"" % bp.GetID() + result += ",modelid=\"%s\"" % modelId + result += ",hitcount=\"%s\"" % bp.GetHitCount() + result += ",threadid=\"%s\"" % bp.GetThreadID() + result += ",oneshot=\"%s\"" % (1 if bp.IsOneShot() else 0) + result += ",enabled=\"%s\"" % (1 if bp.IsEnabled() else 0) + result += ",valid=\"%s\"" % (1 if bp.IsValid() else 0) + result += ",condition=\"%s\"" % ("" if cond is None else cond) + result += ",ignorecount=\"%s\"" % bp.GetIgnoreCount() + result += ",locations=[" + for i in range(bp.GetNumLocations()): + loc = bp.GetLocationAtIndex(i) + addr = loc.GetAddress() + result += "{locid=\"%s\"" % loc.GetID() + result += ",func=\"%s\"" % addr.GetFunction().GetName() + result += ",enabled=\"%s\"" % (1 if loc.IsEnabled() else 0) + result += ",resolved=\"%s\"" % (1 if loc.IsResolved() else 0) + result += ",valid=\"%s\"" % (1 if loc.IsValid() else 0) + result += ",ignorecount=\"%s\"" % loc.GetIgnoreCount() + result += ",addr=\"%s\"}," % loc.GetLoadAddress() + result += "]}," + return result + + def onBreak(): + lldb.debugger.HandleCommand("settings set frame-format ''") + lldb.debugger.HandleCommand("settings set thread-format ''") + result = "*stopped,frame={....}" + print result + + def handleBreakpoints(stuff): + todo = json.loads(stuff) + #dumpJson(todo) + #target = lldb.debugger.CreateTargetWithFileAndArch (exe, lldb.LLDB_ARCH_DEFAULT) + target = lldb.debugger.GetTargetAtIndex(0) + #target = lldb.target + + result = "bkpts={added=[" + + for bp in todo["add"]: + bpType = bp["type"] + if bpType == BreakpointByFileAndLine: + bpNew = target.BreakpointCreateByLocation(str(bp["file"]), int(bp["line"])) + elif bpType == BreakpointByFunction: + bpNew = target.BreakpointCreateByName(bp["function"]) + elif bpType == BreakpointAtMain: + bpNew = target.BreakpointCreateByName("main", target.GetExecutable().GetFilename()) + bpNew.SetIgnoreCount(int(bp["ignorecount"])) + bpNew.SetCondition(str(bp["condition"])) + bpNew.SetEnabled(int(bp["enabled"])) + bpNew.SetOneShot(int(bp["oneshot"])) + #bpNew.SetCallback(breakpoint_function_wrapper, None) + #bpNew.SetCallback(breakpoint_function_wrapper, None) + #"breakpoint command add 1 -o \"import time; print time.asctime()\" + #cmd = "script print(11111111)" + cmd = "continue" + lldb.debugger.HandleCommand( + "breakpoint command add -o 'script onBreak()' %s" % bpNew.GetID()) + + result += dumpBreakpoint(bpNew, bp["modelid"]) + + result += "],changed=[" + + for bp in todo["change"]: + bpChange = target.FindBreakpointByID(int(bp["lldbid"])) + bpChange.SetIgnoreCount(int(bp["ignorecount"])) + bpChange.SetCondition(str(bp["condition"])) + bpChange.SetEnabled(int(bp["enabled"])) + bpChange.SetOneShot(int(bp["oneshot"])) + result += dumpBreakpoint(bpChange, bp["modelid"]) + + result += "],removed=[" + + for bp in todo["remove"]: + bpDead = target.BreakpointDelete(int(bp["lldbid"])) + result += "{modelid=\"%s\"}" % bp["modelid"] + + result += "]}" + return result + + def doStepOver(): + lldb.debugger.SetAsync(False) + lldb.thread.StepOver() + lldb.debugger.SetAsync(True) + result = "result={" + result += "}," + result += stackData({'threadid': lldb.process.selected_thread.id}) + result += threadsData({}) + return result + + def doInterrupt(): + lldb.debugger.SetAsync(False) + lldb.process.Stop() + lldb.debugger.SetAsync(True) + result = "result={" + result += "}" + return result + except: #warn("LOADING LLDB FAILED") pass +#lldb.debugger.HandleCommand('command script add -f ls.ls ls') + +# +#SBEvent data; +#while (!stop) { +#if (self->m_listener.WaitForEvent(UINT32_MAX, data)) { +# if (data.getType() == SBProcess::eBroadcastBitStateChanged && +#m_process.GetStateFromEvent (data) == eStateStopped) { +# SBThread th = m_process.GetSelectedThread(); +# if (th.GetStopReason() == eStopReasonBreakpoint) { +# // th.GetStopReasonDataAtIndex(0) should have the breakpoint id +# } +# } +#} +#} + diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 637c57ff5c2..75a6d7056f0 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -123,7 +123,8 @@ static QString typeToString(BreakpointType type) return BreakHandler::tr("Breakpoint on QML Signal Emit"); case BreakpointAtJavaScriptThrow: return BreakHandler::tr("Breakpoint at JavaScript throw"); - case UnknownType: + case UnknownBreakpointType: + case LastBreakpointType: break; } return BreakHandler::tr("Unknown Breakpoint Type"); @@ -189,7 +190,7 @@ static bool isSimilarTo(const BreakpointParameters &data, const BreakpointRespon { // Clear hit. // Clear miss. - if (needle.type != UnknownType && data.type != UnknownType + if (needle.type != UnknownBreakpointType && data.type != UnknownBreakpointType && data.type != needle.type) return false; @@ -382,7 +383,7 @@ void BreakHandler::loadBreakpoints() if (v.isValid()) data.tracepoint = bool(v.toInt()); v = map.value(_("type")); - if (v.isValid() && v.toInt() != UnknownType) + if (v.isValid() && v.toInt() != UnknownBreakpointType) data.type = BreakpointType(v.toInt()); v = map.value(_("module")); if (v.isValid()) diff --git a/src/plugins/debugger/breakpoint.cpp b/src/plugins/debugger/breakpoint.cpp index da5c6ec80bb..8f050b4ed96 100644 --- a/src/plugins/debugger/breakpoint.cpp +++ b/src/plugins/debugger/breakpoint.cpp @@ -60,6 +60,18 @@ QDebug operator<<(QDebug d, const BreakpointModelId &id) return d; } +BreakpointModelId::BreakpointModelId(const QByteArray &ba) +{ + int pos = ba.indexOf('\''); + if (pos == -1) { + m_majorPart = ba.toUShort(); + m_minorPart = 0; + } else { + m_majorPart = ba.left(pos).toUShort(); + m_minorPart = ba.mid(pos + 1).toUShort(); + } +} + QByteArray BreakpointModelId::toByteArray() const { if (!isValid()) @@ -239,7 +251,8 @@ bool BreakpointParameters::isValid() const break; case WatchpointAtExpression: return !expression.isEmpty(); - case UnknownType: + case UnknownBreakpointType: + case LastBreakpointType: return false; } return true; @@ -315,10 +328,10 @@ QString BreakpointParameters::toString() const case BreakpointAtMain: case BreakpointAtFork: case BreakpointAtExec: - //case BreakpointAtVFork: case BreakpointAtSysCall: case BreakpointAtJavaScriptThrow: - case UnknownType: + case UnknownBreakpointType: + case LastBreakpointType: break; } ts << (enabled ? " [enabled]" : " [disabled]"); diff --git a/src/plugins/debugger/breakpoint.h b/src/plugins/debugger/breakpoint.h index b9317fce90e..8891da055f0 100644 --- a/src/plugins/debugger/breakpoint.h +++ b/src/plugins/debugger/breakpoint.h @@ -120,9 +120,11 @@ QDebug operator<<(QDebug d, const BreakpointResponseId &id); ////////////////////////////////////////////////////////////////// //! \enum Debugger::Internal::BreakpointType + +// Note: Keep synchronized with similar definitions in bridge.py enum BreakpointType { - UnknownType, + UnknownBreakpointType, BreakpointByFileAndLine, BreakpointByFunction, BreakpointByAddress, @@ -131,12 +133,12 @@ enum BreakpointType BreakpointAtMain, BreakpointAtFork, BreakpointAtExec, - //BreakpointAtVFork, BreakpointAtSysCall, WatchpointAtAddress, WatchpointAtExpression, BreakpointOnQmlSignalEmit, - BreakpointAtJavaScriptThrow + BreakpointAtJavaScriptThrow, + LastBreakpointType }; //! \enum Debugger::Internal::BreakpointState @@ -200,7 +202,7 @@ inline void operator|=(BreakpointParts &p, BreakpointParts r) class BreakpointParameters { public: - explicit BreakpointParameters(BreakpointType = UnknownType); + explicit BreakpointParameters(BreakpointType = UnknownBreakpointType); BreakpointParts differencesTo(const BreakpointParameters &rhs) const; bool isValid() const; bool equals(const BreakpointParameters &rhs) const; diff --git a/src/plugins/debugger/breakwindow.cpp b/src/plugins/debugger/breakwindow.cpp index 2f1bf83197e..7805ac51f84 100644 --- a/src/plugins/debugger/breakwindow.cpp +++ b/src/plugins/debugger/breakwindow.cpp @@ -136,7 +136,7 @@ private: }; BreakpointDialog::BreakpointDialog(BreakpointModelId id, QWidget *parent) - : QDialog(parent), m_enabledParts(~0), m_previousType(UnknownType), + : QDialog(parent), m_enabledParts(~0), m_previousType(UnknownBreakpointType), m_firstTypeChange(true) { setWindowTitle(tr("Edit Breakpoint Properties")); @@ -158,7 +158,8 @@ BreakpointDialog::BreakpointDialog(BreakpointModelId id, QWidget *parent) << tr("Break on data access at address given by expression") << tr("Break on QML signal emit") << tr("Break when JavaScript exception is thrown"); - QTC_ASSERT(types.size() == BreakpointAtJavaScriptThrow, return); + // We don't list UnknownBreakpointType, so 1 less: + QTC_CHECK(types.size() + 1 == LastBreakpointType); m_comboBoxType = new QComboBox(groupBoxBasic); m_comboBoxType->setMaxVisibleItems(20); m_comboBoxType->addItems(types); @@ -512,7 +513,8 @@ void BreakpointDialog::typeChanged(int) m_previousType = newType; // Save current state. switch (previousType) { - case UnknownType: + case UnknownBreakpointType: + case LastBreakpointType: break; case BreakpointByFileAndLine: getParts(FileAndLinePart|ModulePart|AllConditionParts|TracePointPart, &m_savedParameters); @@ -542,7 +544,8 @@ void BreakpointDialog::typeChanged(int) // Enable and set up new state from saved values. switch (newType) { - case UnknownType: + case UnknownBreakpointType: + case LastBreakpointType: break; case BreakpointByFileAndLine: setParts(FileAndLinePart|AllConditionParts|ModulePart|TracePointPart, m_savedParameters); diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 78bcff49d25..b26b0db2c33 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -2586,7 +2586,8 @@ bool CdbEngine::acceptsBreakpoint(BreakpointModelId id) const if (!data.isCppBreakpoint()) return false; switch (data.type) { - case UnknownType: + case UnknownBreakpointType: + case LastBreakpointType: case BreakpointAtFork: case WatchpointAtExpression: case BreakpointAtSysCall: diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.cpp b/src/plugins/debugger/cdb/cdbparsehelpers.cpp index 52e456a5758..eb49328c03a 100644 --- a/src/plugins/debugger/cdb/cdbparsehelpers.cpp +++ b/src/plugins/debugger/cdb/cdbparsehelpers.cpp @@ -92,7 +92,8 @@ static inline QString cdbBreakPointFileName(const BreakpointParameters &bp, static BreakpointParameters fixWinMSVCBreakpoint(const BreakpointParameters &p) { switch (p.type) { - case UnknownType: + case UnknownBreakpointType: + case LastBreakpointType: case BreakpointByFileAndLine: case BreakpointByFunction: case BreakpointByAddress: @@ -181,12 +182,13 @@ QByteArray cdbAddBreakpointCommand(const BreakpointParameters &bpIn, case BreakpointAtExec: case WatchpointAtExpression: case BreakpointAtSysCall: - case UnknownType: case BreakpointAtCatch: case BreakpointAtThrow: case BreakpointAtMain: case BreakpointOnQmlSignalEmit: case BreakpointAtJavaScriptThrow: + case UnknownBreakpointType: + case LastBreakpointType: QTC_ASSERT(false, return QByteArray()); break; case BreakpointByAddress: diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 839cebb6866..380777d12cc 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -2540,7 +2540,7 @@ QByteArray GdbEngine::breakpointLocation(BreakpointModelId id) { BreakHandler *handler = breakHandler(); const BreakpointParameters &data = handler->breakpointData(id); - QTC_ASSERT(data.type != UnknownType, return QByteArray()); + QTC_ASSERT(data.type != UnknownBreakpointType, return QByteArray()); // FIXME: Non-GCC-runtime if (data.type == BreakpointAtThrow) return "__cxa_throw"; @@ -3198,7 +3198,7 @@ void GdbEngine::changeBreakpoint(BreakpointModelId id) { BreakHandler *handler = breakHandler(); const BreakpointParameters &data = handler->breakpointData(id); - QTC_ASSERT(data.type != UnknownType, return); + QTC_ASSERT(data.type != UnknownBreakpointType, return); const BreakpointResponse &response = handler->response(id); QTC_ASSERT(response.id.isValid(), return); const QByteArray bpnr = response.id.toByteArray(); diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 4fa781e54d8..06aa88165e0 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -215,6 +215,7 @@ void LldbEngine::setupEngine() postCommand("setting set auto-confirm on"); postCommand("setting set interpreter.prompt-on-quit off"); +#if 0 // Default: // frame-format (string) = "frame #${frame.index}: ${frame.pc} // { ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}} @@ -246,6 +247,7 @@ void LldbEngine::setupEngine() "stopreason='${thread.stop-reason}'" //"returnvalue='${thread.return-value}'" "\\},"); +#endif notifyEngineSetupOk(); } @@ -266,7 +268,12 @@ void LldbEngine::handleInferiorSetup(const LldbResponse &response) void LldbEngine::runEngine() { QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); + m_continuations.append(&LldbEngine::runEngine2); attemptBreakpointSynchronization(); +} + +void LldbEngine::runEngine2() +{ showStatusMessage(tr("Running requested..."), 5000); postCommand("process launch", CB(handleRunEngine)); } @@ -280,7 +287,8 @@ void LldbEngine::handleRunEngine(const LldbResponse &response) void LldbEngine::interruptInferior() { showStatusMessage(tr("Interrupt requested..."), 5000); - postCommand("process launch", CB(handleInferiorInterrupt)); + //postCommand("process interrupt", CB(handleInferiorInterrupt)); + postCommand("script doInterrupt()", CB(handleInferiorInterrupt)); } void LldbEngine::handleInferiorInterrupt(const LldbResponse &response) @@ -288,46 +296,45 @@ void LldbEngine::handleInferiorInterrupt(const LldbResponse &response) Q_UNUSED(response); } -void LldbEngine::executeStep() -{ - resetLocation(); - notifyInferiorRunRequested(); - postCommand("thread step-in", CB(handleContinue)); -} - void LldbEngine::handleContinue(const LldbResponse &response) { Q_UNUSED(response); notifyInferiorRunOk(); } +void LldbEngine::executeStep() +{ + resetLocation(); + notifyInferiorRunRequested(); + postCommand("script doStep()", CB(handleStepOver)); +} + void LldbEngine::executeStepI() { resetLocation(); notifyInferiorRunRequested(); - postCommand("thread step-inst", CB(handleContinue)); + postCommand("script doStepInst()", CB(handleStepOver)); } void LldbEngine::executeStepOut() { resetLocation(); notifyInferiorRunRequested(); - postCommand("thread step-out", CB(handleContinue)); + postCommand("script doStepOut()", CB(handleStepOver)); } void LldbEngine::executeNext() { resetLocation(); notifyInferiorRunRequested(); - postCommand("thread step-over", CB(handleContinue)); + postCommand("script doStepOver()", CB(handleStepOver)); } void LldbEngine::executeNextI() { resetLocation(); notifyInferiorRunRequested(); - notifyInferiorRunOk(); - postCommand("thread step-inst-over", CB(handleContinue)); + postCommand("script doStepInstOver()", CB(handleStepOver)); } void LldbEngine::continueInferior() @@ -337,6 +344,14 @@ void LldbEngine::continueInferior() postCommand("process continue", CB(handleContinue)); } +void LldbEngine::handleStepOver(const LldbResponse &response) +{ + GdbMi all = parseFromString(response.data, "result"); + refreshAll(all); + notifyInferiorRunOk(); + notifyInferiorSpontaneousStop(); +} + void LldbEngine::executeRunToLine(const ContextData &data) { Q_UNUSED(data) @@ -377,56 +392,188 @@ bool LldbEngine::acceptsBreakpoint(BreakpointModelId id) const && startParameters().startMode != AttachCore; } -void LldbEngine::insertBreakpoint(BreakpointModelId id) +void LldbEngine::attemptBreakpointSynchronization() { + showMessage(_("ATTEMPT BREAKPOINT SYNCHRONIZATION")); + if (!stateAcceptsBreakpointChanges()) { + showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE")); + return; + } + BreakHandler *handler = breakHandler(); - QTC_CHECK(handler->state(id) == BreakpointInsertRequested); - handler->notifyBreakpointInsertProceeding(id); - QByteArray loc; - if (handler->type(id) == BreakpointByFunction) - loc = " --name " + handler->functionName(id).toLatin1(); - else - loc = " --file " + handler->fileName(id).toLocal8Bit() - + " --line " + QByteArray::number(handler->lineNumber(id)); + foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) { + // Take ownership of the breakpoint. Requests insertion. + if (acceptsBreakpoint(id)) { + showMessage(_("TAKING OWNERSHIP OF BREAKPOINT %1 IN STATE %2") + .arg(id.toString()).arg(handler->state(id))); + handler->setEngine(id, this); + } else { + showMessage(_("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE") + .arg(id.toString()).arg(handler->state(id))); + } + } - postCommand("break set " + loc, CB(handleBreakInsert), QVariant(id)); + QByteArray toAdd; + QByteArray toChange; + QByteArray toRemove; + + bool done = true; + foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) { + const BreakpointResponse &response = handler->response(id); + switch (handler->state(id)) { + case BreakpointNew: + // Should not happen once claimed. + QTC_CHECK(false); + break; + case BreakpointInsertRequested: + done = false; + toAdd += "{\"modelid\":" + id.toByteArray(); + toAdd += ",\"type\":" + QByteArray::number(handler->type(id)); + toAdd += ",\"ignorecount\":" + QByteArray::number(handler->ignoreCount(id)); + toAdd += ",\"condition\":\"" + handler->condition(id) + '"'; + toAdd += ",\"function\":\"" + handler->functionName(id).toUtf8() + '"'; + toAdd += ",\"oneshot\":" + QByteArray::number(int(handler->isOneShot(id))); + toAdd += ",\"enabled\":" + QByteArray::number(int(handler->isEnabled(id))); + toAdd += ",\"file\":\"" + handler->fileName(id).toUtf8() + '"'; + toAdd += ",\"line\":" + QByteArray::number(handler->lineNumber(id)) + "},"; + handler->notifyBreakpointInsertProceeding(id); + break; + case BreakpointChangeRequested: + done = false; + toChange += "{\"modelid\":" + id.toByteArray(); + toChange += ",\"lldbid\":" + response.id.toByteArray(); + toChange += ",\"type\":" + QByteArray::number(handler->type(id)); + toChange += ",\"ignorecount\":" + QByteArray::number(handler->ignoreCount(id)); + toChange += ",\"condition\":\"" + handler->condition(id) + '"'; + toChange += ",\"function\":\"" + handler->functionName(id).toUtf8() + '"'; + toChange += ",\"oneshot\":" + QByteArray::number(int(handler->isOneShot(id))); + toChange += ",\"enabled\":" + QByteArray::number(int(handler->isEnabled(id))); + toChange += ",\"file\":\"" + handler->fileName(id).toUtf8() + '"'; + toChange += ",\"line\":" + QByteArray::number(handler->lineNumber(id)) + "},"; + handler->notifyBreakpointChangeProceeding(id); + break; + case BreakpointRemoveRequested: + done = false; + toRemove += "{\"modelid\":" + id.toByteArray(); + toRemove += ",\"lldbid\":" + response.id.toByteArray() + "},"; + handler->notifyBreakpointRemoveProceeding(id); + break; + case BreakpointChangeProceeding: + case BreakpointInsertProceeding: + case BreakpointRemoveProceeding: + case BreakpointInserted: + case BreakpointDead: + QTC_CHECK(false); + break; + default: + QTC_ASSERT(false, qDebug() << "UNKNOWN STATE" << id << state()); + } + } + + if (!done) { + showMessage(_("BREAKPOINTS ARE NOT FULLY SYNCHRONIZED")); + if (!toAdd.isEmpty()) + toAdd.chop(1); + if (!toChange.isEmpty()) + toChange.chop(1); + if (!toRemove.isEmpty()) + toRemove.chop(1); + + QByteArray cmd = "script handleBreakpoints('"; + cmd += "{\"add\":[" + toAdd + "]"; + cmd += ",\"change\":[" + toChange + "]"; + cmd += ",\"remove\":[" + toRemove + "]}')"; + postCommand(cmd, CB(handleBreakpointsSynchronized)); + } else { + showMessage(_("BREAKPOINTS ARE SYNCHRONIZED")); + // d->m_disassemblerAgent.updateBreakpointMarkers(); + //LldbResponse dummy; + //handleBreakpointsSynchronized(dummy); + performContinuation(); + } } -void LldbEngine::handleBreakInsert(const LldbResponse &response) +void LldbEngine::performContinuation() { - //qDebug() << "BP RESPONSE: " << response.data; - // Breakpoint 1: where = simple_test_app`main + 62 at simple_test_app.cpp:6699, - // address = 0x08061664 - BreakpointModelId id(response.cookie.toInt()); - BreakHandler *handler = breakHandler(); - QTC_ASSERT(response.data.startsWith("Breakpoint "), return); - int pos1 = response.data.indexOf(':'); - QTC_ASSERT(pos1 != -1, return); - QByteArray bpnr = response.data.mid(11, pos1 - 11); - int pos2 = response.data.lastIndexOf(':'); - QByteArray file = response.data.mid(pos1 + 4, pos2 - pos1 - 4); - QByteArray line = response.data.mid(pos2 + 1); - BreakpointResponse br; - br.id = BreakpointResponseId(bpnr); - br.fileName = _(file); - br.lineNumber = line.toInt(); - handler->setResponse(id, br); - QTC_CHECK(!handler->needsChange(id)); - handler->notifyBreakpointInsertOk(id); + if (!m_continuations.isEmpty()) { + LldbCommandContinuation cont = m_continuations.pop(); + (this->*cont)(); + } } -void LldbEngine::removeBreakpoint(BreakpointModelId id) +void LldbEngine::updateBreakpointData(const GdbMi &bkpt, bool added) { BreakHandler *handler = breakHandler(); - QTC_CHECK(handler->state(id) == BreakpointRemoveRequested); - handler->notifyBreakpointRemoveProceeding(id); - BreakpointResponse br = handler->response(id); - showMessage(_("DELETING BP %1 IN %2").arg(br.id.toString()) - .arg(handler->fileName(id))); - postCommand("break delete " + br.id.toByteArray()); - // Pretend it succeeds without waiting for response. - handler->notifyBreakpointRemoveOk(id); + BreakpointModelId id = BreakpointModelId(bkpt.findChild("modelid").data()); + BreakpointResponse response = handler->response(id); + BreakpointResponseId rid = BreakpointResponseId(bkpt.findChild("lldbid").data()); + if (added) + response.id = rid; + QTC_CHECK(response.id == rid); + response.address = 0; + response.enabled = bkpt.findChild("enabled").data().toInt(); + response.ignoreCount = bkpt.findChild("ignorecount").data().toInt(); + response.condition = bkpt.findChild("condition").data(); + response.hitCount = bkpt.findChild("hitcount").data().toInt(); + + if (added) { + // Added. + GdbMi locations = bkpt.findChild("locations"); + const int numChild = locations.children().size(); + if (numChild > 1) { + foreach (const GdbMi &location, locations.children()) { + BreakpointResponse sub; + sub.id = BreakpointResponseId(rid.majorPart(), + location.findChild("subid").data().toUShort()); + sub.type = response.type; + sub.address = location.findChild("addr").toAddress(); + sub.functionName = QString::fromUtf8(location.findChild("func").data()); + handler->insertSubBreakpoint(id, sub); + } + } else if (numChild == 1) { + const GdbMi location = locations.childAt(0); + response.address = location.findChild("addr").toAddress(); + response.functionName = QString::fromUtf8(location.findChild("func").data()); + } else { + QTC_CHECK(false); + } + handler->setResponse(id, response); + handler->notifyBreakpointInsertOk(id); + } else { + // Changed. + handler->setResponse(id, response); + handler->notifyBreakpointChangeOk(id); + } +} + +void LldbEngine::handleBreakpointsSynchronized(const LldbResponse &response) +{ + BreakHandler *handler = breakHandler(); + GdbMi all = parseFromString(response.data, "bkpts"); + GdbMi bkpts = all.findChild("bkpts"); + GdbMi added = bkpts.findChild("added"); + GdbMi changed = bkpts.findChild("changed"); + GdbMi removed = bkpts.findChild("removed"); + foreach (const GdbMi &bkpt, added.children()) { + BreakpointModelId id = BreakpointModelId(bkpt.findChild("modelid").data()); + QTC_CHECK(handler->state(id) == BreakpointInsertRequested); + updateBreakpointData(bkpt, true); + } + foreach (const GdbMi &bkpt, changed.children()) { + BreakpointModelId id = BreakpointModelId(bkpt.findChild("modelid").data()); + QTC_CHECK(handler->state(id) == BreakpointChangeRequested); + updateBreakpointData(bkpt, false); + } + foreach (const GdbMi &bkpt, removed.children()) { + BreakpointModelId id = BreakpointModelId(bkpt.findChild("modelid").data()); + QTC_CHECK(handler->state(id) == BreakpointRemoveRequested); + handler->notifyBreakpointRemoveOk(id); + } + + // Loop. + //attemptBreakpointSynchronization(); + performContinuation(); } void LldbEngine::loadSymbols(const QString &moduleName) @@ -715,14 +862,24 @@ void LldbEngine::readLldbStandardOutput() // "[KProcess 5564 stopped" // "* thread #1: tid = 0x15bc, 0x0804a17d untitled11`main(argc=1, // argv=0xbfffeff4) + 61 at main.cpp:52, stop reason = breakpoint 1.1 ..." - const int pos = m_inbuffer.indexOf("\x1b[KProcess"); + int pos = m_inbuffer.indexOf("\x1b[KProcess"); if (pos != -1) { - int pid = 0; - const char *format = "\x1b[KProcess %d stopped"; - if (::sscanf(m_inbuffer.constData() + pos, format, &pid) == 1) { - notifyInferiorSpontaneousStop(); - m_inbuffer.clear(); - updateAll(); + pos += 11; + const int pos1 = m_inbuffer.indexOf(' ', pos); + if (pos1 != -1) { + const int pid = m_inbuffer.mid(pos, pos1 - pos).toInt(); + if (pid) { + if (m_inbuffer.mid(pos1 + 1).startsWith("stopped")) { + m_inbuffer.clear(); + notifyInferiorSpontaneousStop(); + gotoLocation(stackHandler()->currentFrame()); + updateAll(); + } else if (m_inbuffer.mid(pos1 + 1).startsWith("exited")) { + m_inbuffer.clear(); + notifyInferiorExited(); + //updateAll(); + } + } } } } @@ -808,7 +965,7 @@ void LldbEngine::updateData(DataKind kind) int maxdepth = debuggerCore()->action(MaximalStackDepth)->value().toInt(); ThreadId curthread = threadsHandler()->currentThread(); stackOptions += "maxdepth:" + QByteArray::number(maxdepth); - stackOptions += "curthread:" + QByteArray::number(curthread.raw()); + stackOptions += ",curthread:" + QByteArray::number(curthread.raw()); } postCommand("script updateData(" + QByteArray::number(kind) + ',' @@ -848,35 +1005,15 @@ void LldbEngine::handleUpdateData(const LldbResponse &response) { //qDebug() << " LOCALS: '" << response.data << "'"; GdbMi all = parseFromString(response.data, "data"); - GdbMi vars = all.findChild("data"); - if (vars.isValid()) { - const bool partial = response.cookie.toBool(); - WatchHandler *handler = watchHandler(); - QList list; + refreshAll(all); +} - if (!partial) { - list.append(*handler->findData("local")); - list.append(*handler->findData("watch")); - list.append(*handler->findData("return")); - } - - foreach (const GdbMi &child, vars.children()) { - WatchData dummy; - dummy.iname = child.findChild("iname").data(); - GdbMi wname = child.findChild("wname"); - if (wname.isValid()) { - // Happens (only) for watched expressions. They are encoded as - // base64 encoded 8 bit data, without quotes - dummy.name = decodeData(wname.data(), Base64Encoded8Bit); - dummy.exp = dummy.name.toUtf8(); - } else { - dummy.name = _(child.findChild("name").data()); - } - parseWatchData(handler->expandedINames(), dummy, child, &list); - } - handler->insertData(list); - } +void LldbEngine::refreshAll(const GdbMi &all) +{ + refreshLocals(all.findChild("data")); + refreshStack(all.findChild("stack")); + refreshThreads(all.findChild("threads")); // const GdbMi typeInfo = all.findChild("typeinfo"); // if (typeInfo.type() == GdbMi::List) { // foreach (const GdbMi &s, typeInfo.children()) { @@ -892,40 +1029,78 @@ void LldbEngine::handleUpdateData(const LldbResponse &response) // if (ti.size) // list[i].size = ti.size; // } +} - GdbMi stack = all.findChild("stack"); - if (stack.isValid()) { - //if (!partial) - // emit stackFrameCompleted(); - StackHandler *handler = stackHandler(); - StackFrames frames; - foreach (const GdbMi &item, stack.findChild("frames").children()) { - StackFrame frame; - frame.level = item.findChild("level").data().toInt(); - frame.file = QString::fromLatin1(item.findChild("file").data()); - frame.function = QString::fromLatin1(item.findChild("func").data()); - frame.from = QString::fromLatin1(item.findChild("func").data()); - frame.line = item.findChild("line").data().toInt(); - frame.address = item.findChild("addr").data().toULongLong(); - frame.usable = QFileInfo(frame.file).isReadable(); - frames.append(frame); - } - bool canExpand = stack.findChild("hasmore").data().toInt(); - debuggerCore()->action(ExpandStack)->setEnabled(canExpand); - handler->setFrames(frames); - } +void LldbEngine::refreshLocals(const GdbMi &vars) +{ + if (!vars.isValid()) + return; - GdbMi threads = all.findChild("threads"); - if (threads.isValid()) { - ThreadsHandler *handler = threadsHandler(); - handler->updateThreads(threads); - if (!handler->currentThread().isValid()) { - ThreadId other = handler->threadAt(0); - if (other.isValid()) - selectThread(other); + //const bool partial = response.cookie.toBool(); + WatchHandler *handler = watchHandler(); + QList list; + + //if (!partial) { + list.append(*handler->findData("local")); + list.append(*handler->findData("watch")); + list.append(*handler->findData("return")); + //} + + foreach (const GdbMi &child, vars.children()) { + WatchData dummy; + dummy.iname = child.findChild("iname").data(); + GdbMi wname = child.findChild("wname"); + if (wname.isValid()) { + // Happens (only) for watched expressions. They are encoded as + // base64 encoded 8 bit data, without quotes + dummy.name = decodeData(wname.data(), Base64Encoded8Bit); + dummy.exp = dummy.name.toUtf8(); + } else { + dummy.name = _(child.findChild("name").data()); } - updateViews(); // Adjust Threads combobox. + parseWatchData(handler->expandedINames(), dummy, child, &list); } + handler->insertData(list); + } + +void LldbEngine::refreshStack(const GdbMi &stack) +{ + if (!stack.isValid()) + return; + + //if (!partial) + // emit stackFrameCompleted(); + StackHandler *handler = stackHandler(); + StackFrames frames; + foreach (const GdbMi &item, stack.findChild("frames").children()) { + StackFrame frame; + frame.level = item.findChild("level").data().toInt(); + frame.file = QString::fromLatin1(item.findChild("file").data()); + frame.function = QString::fromLatin1(item.findChild("func").data()); + frame.from = QString::fromLatin1(item.findChild("func").data()); + frame.line = item.findChild("line").data().toInt(); + frame.address = item.findChild("addr").data().toULongLong(); + frame.usable = QFileInfo(frame.file).isReadable(); + frames.append(frame); + } + bool canExpand = stack.findChild("hasmore").data().toInt(); + debuggerCore()->action(ExpandStack)->setEnabled(canExpand); + handler->setFrames(frames); + } + +void LldbEngine::refreshThreads(const GdbMi &threads) +{ + if (!threads.isValid()) + return; + + ThreadsHandler *handler = threadsHandler(); + handler->updateThreads(threads); + if (!handler->currentThread().isValid()) { + ThreadId other = handler->threadAt(0); + if (other.isValid()) + selectThread(other); + } + updateViews(); // Adjust Threads combobox. } void LldbEngine::loadPythonDumpers() diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index 04822ce9173..51fef9f618c 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -33,6 +33,7 @@ #include "debuggerengine.h" #include +#include #include #include @@ -60,6 +61,7 @@ class LldbEngine : public DebuggerEngine public: explicit LldbEngine(const DebuggerStartParameters &startParameters); ~LldbEngine(); + private: // DebuggerEngine implementation void executeStep(); @@ -71,6 +73,7 @@ private: void setupEngine(); void setupInferior(); void runEngine(); + void runEngine2(); void shutdownInferior(); void shutdownEngine(); @@ -88,8 +91,7 @@ private: void selectThread(ThreadId threadId); bool acceptsBreakpoint(BreakpointModelId id) const; - void insertBreakpoint(BreakpointModelId id); - void removeBreakpoint(BreakpointModelId id); + void attemptBreakpointSynchronization(); void assignValueInDebugger(const WatchData *data, const QString &expr, const QVariant &value); @@ -107,6 +109,9 @@ private: bool isSynchronous() const { return true; } void updateWatchData(const WatchData &data, const WatchUpdateFlags &flags); + void performContinuation(); + void handleStepOver(const LldbResponse &response); + signals: void outputReady(const QByteArray &data); @@ -120,6 +125,10 @@ private: Q_SLOT void readLldbStandardError(); Q_SLOT void handleOutput2(const QByteArray &data); void handleResponse(const QByteArray &ba); + void refreshAll(const GdbMi &all); + void refreshThreads(const GdbMi &threads); + void refreshStack(const GdbMi &stack); + void refreshLocals(const GdbMi &vars); enum DataKind { LocalsData = 1, StackData = 2, ThreadData = 4 }; @@ -137,6 +146,7 @@ private: typedef void (LldbEngine::*LldbCommandCallback) (const LldbResponse &response); + typedef void (LldbEngine::*LldbCommandContinuation)(); struct LldbCommand { @@ -153,7 +163,8 @@ private: void handleListLocals(const LldbResponse &response); void handleListModules(const LldbResponse &response); void handleListSymbols(const LldbResponse &response); - void handleBreakInsert(const LldbResponse &response); + void handleBreakpointsSynchronized(const LldbResponse &response); + void updateBreakpointData(const GdbMi &bkpt, bool added); void handleUpdateStack(const LldbResponse &response); void handleUpdateThreads(const LldbResponse &response); @@ -167,6 +178,7 @@ private: GdbMi parseFromString(QByteArray out, const QByteArray &firstTopLevel); QQueue m_commands; + QStack m_continuations; QByteArray m_inbuffer; QString m_scriptFileName;