Debugger: Re-use base infrastructure for LLDB breakpoint handling

Change-Id: If32b1f421e45dc4ee446e193e03c959d7c700948
Reviewed-by: hjk <hjk@theqtcompany.com>
This commit is contained in:
hjk
2015-02-02 13:48:33 +01:00
parent d9af2e0551
commit 3a9b34f232
3 changed files with 118 additions and 152 deletions

View File

@@ -643,7 +643,8 @@ class Dumper(DumperBase):
self.target = self.debugger.CreateTarget(None, None, None, True, error) self.target = self.debugger.CreateTarget(None, None, None, True, error)
if self.target.IsValid(): if self.target.IsValid():
self.handleBreakpoints(args) for bp in args['bkpts']:
self.insertBreakpoint(bp)
state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed" state = "inferiorsetupok" if self.target.IsValid() else "inferiorsetupfailed"
self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_)) self.report('state="%s",msg="%s",exe="%s"' % (state, error, self.executable_))
@@ -1309,7 +1310,9 @@ class Dumper(DumperBase):
return self.target.BreakpointCreateByName( return self.target.BreakpointCreateByName(
"main", self.target.GetExecutable().GetFilename()) "main", self.target.GetExecutable().GetFilename())
def addBreakpoint(self, args): def insertBreakpoint(self, args):
more = True
modelId = args['modelid']
bpType = args["type"] bpType = args["type"]
if bpType == BreakpointByFileAndLine: if bpType == BreakpointByFileAndLine:
bp = self.target.BreakpointCreateByLocation( bp = self.target.BreakpointCreateByLocation(
@@ -1342,11 +1345,14 @@ class Dumper(DumperBase):
bp = self.target.WatchAddress(value.GetLoadAddress(), bp = self.target.WatchAddress(value.GetLoadAddress(),
value.GetByteSize(), False, True, error) value.GetByteSize(), False, True, error)
except: except:
return self.target.BreakpointCreateByName(None) bp = self.target.BreakpointCreateByName(None)
else: else:
# This leaves the unhandled breakpoint in a (harmless) # This leaves the unhandled breakpoint in a (harmless)
# "pending" state. # "pending" state.
return self.target.BreakpointCreateByName(None) bp = self.target.BreakpointCreateByName(None)
more = False
if more:
bp.SetIgnoreCount(int(args["ignorecount"])) bp.SetIgnoreCount(int(args["ignorecount"]))
if hasattr(bp, 'SetCondition'): if hasattr(bp, 'SetCondition'):
bp.SetCondition(self.hexdecode(args["condition"])) bp.SetCondition(self.hexdecode(args["condition"]))
@@ -1354,57 +1360,31 @@ class Dumper(DumperBase):
if hasattr(bp, 'SetOneShot'): if hasattr(bp, 'SetOneShot'):
bp.SetOneShot(int(args["oneshot"])) bp.SetOneShot(int(args["oneshot"]))
self.breakpointsToCheck.add(bp.GetID()) self.breakpointsToCheck.add(bp.GetID())
return bp self.report('breakpoint-added={%s,modelid="%s"}' % (self.describeBreakpoint(bp), modelId))
def changeBreakpoint(self, args): def changeBreakpoint(self, args):
id = int(args["lldbid"]) lldbId = int(args["lldbid"])
if id > qqWatchpointOffset: modelId = args['modelid']
bp = self.target.FindWatchpointByID(id) if lldbId > qqWatchpointOffset:
bp = self.target.FindWatchpointByID(lldbId)
else: else:
bp = self.target.FindBreakpointByID(id) bp = self.target.FindBreakpointByID(lldbId)
bp.SetIgnoreCount(int(args["ignorecount"])) bp.SetIgnoreCount(int(args["ignorecount"]))
bp.SetCondition(self.hexdecode(args["condition"])) bp.SetCondition(self.hexdecode(args["condition"]))
bp.SetEnabled(int(args["enabled"])) bp.SetEnabled(int(args["enabled"]))
if hasattr(bp, 'SetOneShot'): if hasattr(bp, 'SetOneShot'):
bp.SetOneShot(int(args["oneshot"])) bp.SetOneShot(int(args["oneshot"]))
return bp self.report('breakpoint-changed={%s,modelid="%s"}'
% (self.describeBreakpoint(bp), modelId))
def removeBreakpoint(self, args): def removeBreakpoint(self, args):
id = int(args['lldbid']) lldbId = int(args['lldbid'])
if id > qqWatchpointOffset: modelId = args['modelid']
return self.target.DeleteWatchpoint(id - qqWatchpointOffset) if lldbId > qqWatchpointOffset:
return self.target.BreakpointDelete(id) res = self.target.DeleteWatchpoint(lldbId - qqWatchpointOffset)
res = self.target.BreakpointDelete(lldbId)
def handleBreakpoints(self, args):
# This seems to be only needed on Linux.
needStop = False
if self.process and platform.system() == "Linux":
needStop = self.process.GetState() != lldb.eStateStopped
if needStop:
error = self.process.Stop()
for bp in args['bkpts']:
operation = bp['operation']
modelId = bp['modelid']
if operation == 'add':
bpNew = self.addBreakpoint(bp)
self.report('breakpoint-added={%s,modelid="%s"}'
% (self.describeBreakpoint(bpNew), modelId))
elif operation == 'change':
bpNew = self.changeBreakpoint(bp)
self.report('breakpoint-changed={%s,modelid="%s"}'
% (self.describeBreakpoint(bpNew), modelId))
elif operation == 'remove':
bpDead = self.removeBreakpoint(bp)
self.report('breakpoint-removed={modelid="%s"}' % modelId) self.report('breakpoint-removed={modelid="%s"}' % modelId)
if needStop:
error = self.process.Continue()
def listModules(self, args): def listModules(self, args):
result = 'modules=[' result = 'modules=['
for i in xrange(self.target.GetNumModules()): for i in xrange(self.target.GetNumModules()):

View File

@@ -309,7 +309,21 @@ void LldbEngine::setupInferior()
cmd.arg("useTerminal", sp.useTerminal); cmd.arg("useTerminal", sp.useTerminal);
cmd.arg("startMode", sp.startMode); cmd.arg("startMode", sp.startMode);
attemptBreakpointSynchronizationHelper(&cmd); cmd.beginList("bkpts");
foreach (Breakpoint bp, breakHandler()->unclaimedBreakpoints()) {
if (acceptsBreakpoint(bp)) {
showMessage(_("TAKING OWNERSHIP OF BREAKPOINT %1 IN STATE %2")
.arg(bp.id().toString()).arg(bp.state()));
bp.setEngine(this);
cmd.beginGroup();
insertBreakpointHelper(&cmd, bp);
cmd.endGroup();
} else {
showMessage(_("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE")
.arg(bp.id().toString()).arg(bp.state()));
}
}
cmd.endList();
cmd.beginList("processArgs"); cmd.beginList("processArgs");
foreach (const QString &arg, args.toUnixArgs()) foreach (const QString &arg, args.toUnixArgs())
@@ -520,113 +534,81 @@ void LldbEngine::selectThread(ThreadId threadId)
runCommand(DebuggerCommand("selectThread").arg("id", threadId.raw())); runCommand(DebuggerCommand("selectThread").arg("id", threadId.raw()));
} }
bool LldbEngine::stateAcceptsBreakpointChanges() const
{
switch (state()) {
case InferiorSetupRequested:
case InferiorRunRequested:
case InferiorRunOk:
case InferiorStopRequested:
case InferiorStopOk:
return true;
default:
return false;
}
}
bool LldbEngine::acceptsBreakpoint(Breakpoint bp) const bool LldbEngine::acceptsBreakpoint(Breakpoint bp) const
{ {
return bp.parameters().isCppBreakpoint() && startParameters().startMode != AttachCore; if (startParameters().startMode == AttachCore)
return false;
// We handle QML breakpoint unless specifically disabled.
if (isNativeMixedEnabled() && !(startParameters().languages & QmlLanguage))
return true;
return bp.parameters().isCppBreakpoint();
} }
bool LldbEngine::attemptBreakpointSynchronizationHelper(DebuggerCommand *cmd) void LldbEngine::insertBreakpoint(Breakpoint bp)
{ {
BreakHandler *handler = breakHandler(); DebuggerCommand cmd("insertBreakpoint");
insertBreakpointHelper(&cmd, bp);
foreach (Breakpoint bp, handler->unclaimedBreakpoints()) { runCommand(cmd);
// Take ownership of the breakpoint. Requests insertion. }
if (acceptsBreakpoint(bp)) {
showMessage(_("TAKING OWNERSHIP OF BREAKPOINT %1 IN STATE %2") void LldbEngine::insertBreakpointHelper(DebuggerCommand *cmd, Breakpoint bp) const
.arg(bp.id().toString()).arg(bp.state())); {
bp.setEngine(this); cmd->arg("modelid", bp.id().toByteArray());
} else { cmd->arg("type", bp.type());
showMessage(_("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE") cmd->arg("ignorecount", bp.ignoreCount());
.arg(bp.id().toString()).arg(bp.state())); cmd->arg("condition", bp.condition().toHex());
} cmd->arg("function", bp.functionName().toUtf8());
} cmd->arg("oneshot", bp.isOneShot());
cmd->arg("enabled", bp.isEnabled());
bool done = true; cmd->arg("file", bp.fileName().toUtf8());
cmd->beginList("bkpts"); cmd->arg("line", bp.lineNumber());
foreach (Breakpoint bp, handler->engineBreakpoints(this)) { cmd->arg("address", bp.address());
const BreakpointResponse &response = bp.response(); cmd->arg("expression", bp.expression());
const BreakpointState bpState = bp.state(); bp.notifyBreakpointInsertProceeding();
switch (bpState) { }
case BreakpointNew:
// Should not happen once claimed. void LldbEngine::changeBreakpoint(Breakpoint bp)
QTC_CHECK(false); {
break; const BreakpointResponse &response = bp.response();
case BreakpointInsertRequested: DebuggerCommand cmd("changeBreakpoint");
done = false; cmd.arg("modelid", bp.id().toByteArray());
cmd->beginGroup() cmd.arg("lldbid", response.id.toByteArray());
.arg("operation", "add") cmd.arg("type", bp.type());
.arg("modelid", bp.id().toByteArray()) cmd.arg("ignorecount", bp.ignoreCount());
.arg("type", bp.type()) cmd.arg("condition", bp.condition().toHex());
.arg("ignorecount", bp.ignoreCount()) cmd.arg("function", bp.functionName().toUtf8());
.arg("condition", bp.condition().toHex()) cmd.arg("oneshot", bp.isOneShot());
.arg("function", bp.functionName().toUtf8()) cmd.arg("enabled", bp.isEnabled());
.arg("oneshot", bp.isOneShot()) cmd.arg("file", bp.fileName().toUtf8());
.arg("enabled", bp.isEnabled()) cmd.arg("line", bp.lineNumber());
.arg("file", bp.fileName().toUtf8()) cmd.arg("address", bp.address());
.arg("line", bp.lineNumber()) cmd.arg("expression", bp.expression());
.arg("address", bp.address()) bp.notifyBreakpointChangeProceeding();
.arg("expression", bp.expression()) runCommand(cmd);
.endGroup(); }
bp.notifyBreakpointInsertProceeding();
break; void LldbEngine::removeBreakpoint(Breakpoint bp)
case BreakpointChangeRequested: {
done = false; const BreakpointResponse &response = bp.response();
cmd->beginGroup() DebuggerCommand cmd("removeBreakpoint");
.arg("operation", "change") cmd.arg("modelid", bp.id().toByteArray());
.arg("modelid", bp.id().toByteArray()) cmd.arg("lldbid", response.id.toByteArray());
.arg("lldbid", response.id.toByteArray()) bp.notifyBreakpointRemoveProceeding();
.arg("type", bp.type())
.arg("ignorecount", bp.ignoreCount())
.arg("condition", bp.condition().toHex())
.arg("function", bp.functionName().toUtf8())
.arg("oneshot", bp.isOneShot())
.arg("enabled", bp.isEnabled())
.arg("file", bp.fileName().toUtf8())
.arg("line", bp.lineNumber())
.arg("address", bp.address())
.arg("expression", bp.expression())
.endGroup();
bp.notifyBreakpointChangeProceeding();
break;
case BreakpointRemoveRequested:
done = false;
cmd->beginGroup()
.arg("operation", "remove")
.arg("modelid", bp.id().toByteArray())
.arg("lldbid", response.id.toByteArray())
.endGroup();
bp.notifyBreakpointRemoveProceeding();
break;
case BreakpointChangeProceeding:
case BreakpointInsertProceeding:
case BreakpointRemoveProceeding:
case BreakpointInserted:
case BreakpointDead:
QTC_ASSERT(false, qDebug() << "UNEXPECTED STATE" << bpState << "FOR BP " << bp.id());
break;
default:
QTC_ASSERT(false, qDebug() << "UNKNOWN STATE" << bpState << "FOR BP" << bp.id());
}
}
cmd->endList();
return done;
}
void LldbEngine::attemptBreakpointSynchronization()
{
showMessage(_("ATTEMPT BREAKPOINT SYNCHRONIZATION"));
if (!stateAcceptsBreakpointChanges()) {
showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
return;
}
DebuggerCommand cmd("handleBreakpoints");
if (!attemptBreakpointSynchronizationHelper(&cmd)) {
showMessage(_("BREAKPOINTS ARE NOT FULLY SYNCHRONIZED"));
runCommand(cmd); runCommand(cmd);
} else {
showMessage(_("BREAKPOINTS ARE SYNCHRONIZED"));
}
} }
void LldbEngine::updateBreakpointData(const GdbMi &bkpt, bool added) void LldbEngine::updateBreakpointData(const GdbMi &bkpt, bool added)

View File

@@ -95,9 +95,13 @@ private:
void activateFrame(int index); void activateFrame(int index);
void selectThread(ThreadId threadId); void selectThread(ThreadId threadId);
// This should be always the last call in a function.
bool stateAcceptsBreakpointChanges() const;
bool acceptsBreakpoint(Breakpoint bp) const; bool acceptsBreakpoint(Breakpoint bp) const;
void attemptBreakpointSynchronization(); void insertBreakpoint(Breakpoint bp);
bool attemptBreakpointSynchronizationHelper(DebuggerCommand *command); void insertBreakpointHelper(DebuggerCommand *cmd, Breakpoint bp) const;
void removeBreakpoint(Breakpoint bp);
void changeBreakpoint(Breakpoint bp);
void assignValueInDebugger(const WatchData *data, void assignValueInDebugger(const WatchData *data,
const QString &expr, const QVariant &value); const QString &expr, const QVariant &value);