Debugger: Add basic breakpoint handling and stepping to LLDB backend

Change-Id: Ib700afa63739e6d26bdd97225265559d7112eadb
Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
hjk
2013-04-09 10:59:36 +02:00
parent 46b9c03cbc
commit 87f2d83ec2
10 changed files with 496 additions and 139 deletions

View File

@@ -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
# }
# }
#}
#}

View File

@@ -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())

View File

@@ -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]");

View File

@@ -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;

View File

@@ -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);

View File

@@ -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:

View File

@@ -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:

View File

@@ -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();

View File

@@ -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);
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)
@@ -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()) {
@@ -925,7 +1101,6 @@ void LldbEngine::handleUpdateData(const LldbResponse &response)
selectThread(other);
}
updateViews(); // Adjust Threads combobox.
}
}
void LldbEngine::loadPythonDumpers()

View File

@@ -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;