forked from qt-creator/qt-creator
Debugger: Add basic breakpoint handling and stepping to LLDB backend
Change-Id: Ib700afa63739e6d26bdd97225265559d7112eadb Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
@@ -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
|
||||
# }
|
||||
# }
|
||||
#}
|
||||
#}
|
||||
|
||||
|
||||
|
@@ -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())
|
||||
|
@@ -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]");
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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:
|
||||
|
@@ -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:
|
||||
|
@@ -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();
|
||||
|
@@ -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,58 +392,190 @@ 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);
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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));
|
||||
|
||||
postCommand("break set " + loc, CB(handleBreakInsert), QVariant(id));
|
||||
}
|
||||
|
||||
void LldbEngine::handleBreakInsert(const LldbResponse &response)
|
||||
{
|
||||
//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);
|
||||
}
|
||||
|
||||
void LldbEngine::removeBreakpoint(BreakpointModelId id)
|
||||
{
|
||||
BreakHandler *handler = breakHandler();
|
||||
QTC_CHECK(handler->state(id) == BreakpointRemoveRequested);
|
||||
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);
|
||||
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.
|
||||
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::performContinuation()
|
||||
{
|
||||
if (!m_continuations.isEmpty()) {
|
||||
LldbCommandContinuation cont = m_continuations.pop();
|
||||
(this->*cont)();
|
||||
}
|
||||
}
|
||||
|
||||
void LldbEngine::updateBreakpointData(const GdbMi &bkpt, bool added)
|
||||
{
|
||||
BreakHandler *handler = breakHandler();
|
||||
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)
|
||||
{
|
||||
Q_UNUSED(moduleName)
|
||||
@@ -715,14 +862,24 @@ void LldbEngine::readLldbStandardOutput()
|
||||
// "<Esc>[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();
|
||||
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,17 +1005,46 @@ 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();
|
||||
refreshAll(all);
|
||||
}
|
||||
|
||||
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()) {
|
||||
// const GdbMi name = s.findChild("name");
|
||||
// const GdbMi size = s.findChild("size");
|
||||
// if (name.isValid() && size.isValid())
|
||||
// m_typeInfoCache.insert(QByteArray::fromBase64(name.data()),
|
||||
// TypeInfo(size.data().toUInt()));
|
||||
// }
|
||||
// }
|
||||
// for (int i = 0; i != list.size(); ++i) {
|
||||
// const TypeInfo ti = m_typeInfoCache.value(list.at(i).type);
|
||||
// if (ti.size)
|
||||
// list[i].size = ti.size;
|
||||
// }
|
||||
}
|
||||
|
||||
void LldbEngine::refreshLocals(const GdbMi &vars)
|
||||
{
|
||||
if (!vars.isValid())
|
||||
return;
|
||||
|
||||
//const bool partial = response.cookie.toBool();
|
||||
WatchHandler *handler = watchHandler();
|
||||
QList<WatchData> list;
|
||||
|
||||
if (!partial) {
|
||||
//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;
|
||||
@@ -877,24 +1063,11 @@ void LldbEngine::handleUpdateData(const LldbResponse &response)
|
||||
handler->insertData(list);
|
||||
}
|
||||
|
||||
// const GdbMi typeInfo = all.findChild("typeinfo");
|
||||
// if (typeInfo.type() == GdbMi::List) {
|
||||
// foreach (const GdbMi &s, typeInfo.children()) {
|
||||
// const GdbMi name = s.findChild("name");
|
||||
// const GdbMi size = s.findChild("size");
|
||||
// if (name.isValid() && size.isValid())
|
||||
// m_typeInfoCache.insert(QByteArray::fromBase64(name.data()),
|
||||
// TypeInfo(size.data().toUInt()));
|
||||
// }
|
||||
// }
|
||||
// for (int i = 0; i != list.size(); ++i) {
|
||||
// const TypeInfo ti = m_typeInfoCache.value(list.at(i).type);
|
||||
// if (ti.size)
|
||||
// list[i].size = ti.size;
|
||||
// }
|
||||
void LldbEngine::refreshStack(const GdbMi &stack)
|
||||
{
|
||||
if (!stack.isValid())
|
||||
return;
|
||||
|
||||
GdbMi stack = all.findChild("stack");
|
||||
if (stack.isValid()) {
|
||||
//if (!partial)
|
||||
// emit stackFrameCompleted();
|
||||
StackHandler *handler = stackHandler();
|
||||
@@ -915,8 +1088,11 @@ void LldbEngine::handleUpdateData(const LldbResponse &response)
|
||||
handler->setFrames(frames);
|
||||
}
|
||||
|
||||
GdbMi threads = all.findChild("threads");
|
||||
if (threads.isValid()) {
|
||||
void LldbEngine::refreshThreads(const GdbMi &threads)
|
||||
{
|
||||
if (!threads.isValid())
|
||||
return;
|
||||
|
||||
ThreadsHandler *handler = threadsHandler();
|
||||
handler->updateThreads(threads);
|
||||
if (!handler->currentThread().isValid()) {
|
||||
@@ -926,7 +1102,6 @@ void LldbEngine::handleUpdateData(const LldbResponse &response)
|
||||
}
|
||||
updateViews(); // Adjust Threads combobox.
|
||||
}
|
||||
}
|
||||
|
||||
void LldbEngine::loadPythonDumpers()
|
||||
{
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include "debuggerengine.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QStack>
|
||||
#include <QQueue>
|
||||
#include <QVariant>
|
||||
|
||||
@@ -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<LldbCommand> m_commands;
|
||||
QStack<LldbCommandContinuation> m_continuations;
|
||||
|
||||
QByteArray m_inbuffer;
|
||||
QString m_scriptFileName;
|
||||
|
Reference in New Issue
Block a user