Commit d4a32fd6 authored by hjk's avatar hjk

Debugger: Implement native mixed breakpoints with LLDB

Breakpoints are hit, stack frames are identified as JS or native.
No further data yet.

Change-Id: I84a02422fd36dc7645003114dd8519bedd913c06
Reviewed-by: default avatarhjk <hjk@theqtcompany.com>
parent 955e28f9
...@@ -1718,10 +1718,14 @@ class DumperBase: ...@@ -1718,10 +1718,14 @@ class DumperBase:
expr = 'qt_v4DebuggerHook("{%s}")' % data expr = 'qt_v4DebuggerHook("{%s}")' % data
try: try:
res = self.parseAndEvaluate(expr) res = self.parseAndEvaluate(expr)
#print("QML command ok, RES: %s, CMD: %s" % (res, expr)) print("QML command ok, RES: %s, CMD: %s" % (res, expr))
except RuntimeError as error: except RuntimeError as error:
#print("QML command failed: %s: %s" % (expr, error)) #print("QML command failed: %s: %s" % (expr, error))
res = None res = None
except AttributeError as error:
# Happens with LLDB and 'None' current thread.
#print("QML command failed: %s: %s" % (expr, error))
res = None
return res return res
def prepareQmlStep(self, _): def prepareQmlStep(self, _):
...@@ -1765,6 +1769,16 @@ class DumperBase: ...@@ -1765,6 +1769,16 @@ class DumperBase:
print("Resolving QML breakpoint: %s" % bp) print("Resolving QML breakpoint: %s" % bp)
return int(bp) return int(bp)
def isInternalQmlFrame(self, functionName):
if functionName is None:
return False
if functionName.startswith("qt_v4"):
return True
return functionName.startswith(self.qtNamespace() + "QV4::")
def isReportableQmlFrame(self, functionName):
return functionName and functionName.find("QV4::Moth::VME::exec") >= 0
# Some "Enums" # Some "Enums"
......
...@@ -1752,14 +1752,6 @@ class Dumper(DumperBase): ...@@ -1752,14 +1752,6 @@ class Dumper(DumperBase):
'fullname="%s",line="%s",language="js",addr="0x%x"}') 'fullname="%s",line="%s",language="js",addr="0x%x"}')
% (level, functionName, fileName, fileName, lineNumber, context)) % (level, functionName, fileName, fileName, lineNumber, context))
def isInternalQmlFrame(self, functionName):
if functionName.startswith("qt_v4"):
return True
return functionName.startswith(self.qtNamespace() + "QV4::")
def isReportableQmlFrame(self, functionName):
return functionName.find("QV4::Moth::VME::exec") >= 0
def stackListFrames(self, n, options): def stackListFrames(self, n, options):
self.prepare("options:" + options + ",pe") self.prepare("options:" + options + ",pe")
self.output = [] self.output = []
......
...@@ -256,6 +256,9 @@ class Dumper(DumperBase): ...@@ -256,6 +256,9 @@ class Dumper(DumperBase):
self.isInterrupting_ = False self.isInterrupting_ = False
self.dummyValue = None self.dummyValue = None
self.breakpointsToCheck = set([]) self.breakpointsToCheck = set([])
self.qmlBreakpointResolvers = {}
self.qmlTriggeredBreakpoint = None
self.nativeMixed = False
def enterSubItem(self, item): def enterSubItem(self, item):
if isinstance(item.name, lldb.SBValue): if isinstance(item.name, lldb.SBValue):
...@@ -809,15 +812,41 @@ class Dumper(DumperBase): ...@@ -809,15 +812,41 @@ class Dumper(DumperBase):
if not frame.IsValid(): if not frame.IsValid():
isLimited = False isLimited = False
break break
lineEntry = frame.GetLineEntry() lineEntry = frame.GetLineEntry()
line = lineEntry.GetLine() lineNumber = lineEntry.GetLine()
result += '{pc="0x%x"' % frame.GetPC()
result += ',level="%d"' % frame.idx pc = frame.GetPC()
result += ',addr="0x%x"' % frame.GetPCAddress().GetLoadAddress(self.target) level = frame.idx
result += ',func="%s"' % frame.GetFunctionName() addr = frame.GetPCAddress().GetLoadAddress(self.target)
result += ',line="%d"' % line functionName = frame.GetFunctionName()
result += ',fullname="%s"' % fileName(lineEntry.file) fullname = fileName(lineEntry.file)
result += ',file="%s"},' % fileName(lineEntry.file) usable = None
language = None
if self.nativeMixed:
if self.isReportableQmlFrame(functionName):
#self.putQmlLocation(i, frame, sal)
functionName = "### JS ###";
language = "js"
elif not functionName is None:
if functionName.startswith("qt_v4"):
usable = 0
elif functionName.find("QV4::") >= 0:
usable = 0
result += '{pc="0x%x"' % pc
result += ',level="%d"' % level
result += ',addr="0x%x"' % addr
if not usable is None:
result += ',usable="%s"' % usable
result += ',func="%s"' % functionName
result += ',line="%d"' % lineNumber
if not language is None:
result += ',language="%s"' % language
result += ',fullname="%s"' % fullname
result += ',file="%s"},' % fullname
result += ']' result += ']'
result += ',hasmore="%d"' % isLimited result += ',hasmore="%d"' % isLimited
result += ',limit="%d"' % limit result += ',limit="%d"' % limit
...@@ -1216,8 +1245,9 @@ class Dumper(DumperBase): ...@@ -1216,8 +1245,9 @@ class Dumper(DumperBase):
msg = lldb.SBEvent.GetCStringFromEvent(event) msg = lldb.SBEvent.GetCStringFromEvent(event)
flavor = event.GetDataFlavor() flavor = event.GetDataFlavor()
state = lldb.SBProcess.GetStateFromEvent(event) state = lldb.SBProcess.GetStateFromEvent(event)
self.report('event={type="%s",data="%s",msg="%s",flavor="%s",state="%s"}' bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
% (eventType, out.GetData(), msg, flavor, state)) self.report('event={type="%s",data="%s",msg="%s",flavor="%s",state="%s",bp="%s"}'
% (eventType, out.GetData(), msg, flavor, state, bp))
if state != self.eventState: if state != self.eventState:
self.eventState = state self.eventState = state
if state == lldb.eStateExited: if state == lldb.eStateExited:
...@@ -1228,12 +1258,32 @@ class Dumper(DumperBase): ...@@ -1228,12 +1258,32 @@ class Dumper(DumperBase):
self.report('exited={status="%s",desc="%s"}' self.report('exited={status="%s",desc="%s"}'
% (self.process.GetExitStatus(), self.process.GetExitDescription())) % (self.process.GetExitStatus(), self.process.GetExitDescription()))
elif state == lldb.eStateStopped: elif state == lldb.eStateStopped:
stoppedThread = self.firstStoppedThread()
if stoppedThread:
#self.report("STOPPED THREAD: %s" % stoppedThread)
frame = stoppedThread.GetFrameAtIndex(0)
#self.report("FRAME: %s" % frame)
function = frame.GetFunction()
#self.report("FUNCTION: %s" % function)
if function.GetName() == "qt_v4ResolvePendingBreakpointsHook":
#self.report("RESOLVER HIT")
for bp in self.qmlBreakpointResolvers:
self.qmlBreakpointResolvers[bp]()
self.target.BreakpointDelete(bp.GetID())
self.qmlBreakpointResolvers = {}
self.process.Continue();
return
if self.isInterrupting_: if self.isInterrupting_:
self.isInterrupting_ = False self.isInterrupting_ = False
self.reportState("inferiorstopok") self.reportState("inferiorstopok")
elif self.ignoreStops > 0: elif self.ignoreStops > 0:
self.ignoreStops -= 1 self.ignoreStops -= 1
self.process.Continue() self.process.Continue()
#elif bp and bp in self.qmlBreakpointResolvers:
# self.report("RESOLVER HIT")
# self.qmlBreakpointResolvers[bp]()
# self.process.Continue();
else: else:
self.reportState("stopped") self.reportState("stopped")
else: else:
...@@ -1311,20 +1361,24 @@ class Dumper(DumperBase): ...@@ -1311,20 +1361,24 @@ class Dumper(DumperBase):
"main", self.target.GetExecutable().GetFilename()) "main", self.target.GetExecutable().GetFilename())
def insertBreakpoint(self, args): def insertBreakpoint(self, args):
bpType = args["type"]
if bpType == BreakpointByFileAndLine:
fileName = args["fileName"]
if fileName.endswith(".js") or fileName.endswith(".qml"):
self.insertQmlBreakpoint(args)
return
more = True more = True
modelId = args['modelid'] modelId = args['modelid']
bpType = args["type"]
if bpType == BreakpointByFileAndLine: if bpType == BreakpointByFileAndLine:
bp = self.target.BreakpointCreateByLocation( bp = self.target.BreakpointCreateByLocation(
str(args["file"]), int(args["line"])) str(args["fileName"]), int(args["lineNumber"]))
elif bpType == BreakpointByFunction: elif bpType == BreakpointByFunction:
bp = self.target.BreakpointCreateByName(args["function"]) bp = self.target.BreakpointCreateByName(args["function"])
elif bpType == BreakpointByAddress: elif bpType == BreakpointByAddress:
bp = self.target.BreakpointCreateByAddress(args["address"]) bp = self.target.BreakpointCreateByAddress(args["address"])
elif bpType == BreakpointAtMain: elif bpType == BreakpointAtMain:
bp = self.createBreakpointAtMain() bp = self.createBreakpointAtMain()
elif bpType == BreakpointByFunction:
bp = self.target.BreakpointCreateByName(args["function"])
elif bpType == BreakpointAtThrow: elif bpType == BreakpointAtThrow:
bp = self.target.BreakpointCreateForException( bp = self.target.BreakpointCreateForException(
lldb.eLanguageTypeC_plus_plus, False, True) lldb.eLanguageTypeC_plus_plus, False, True)
...@@ -1621,27 +1675,22 @@ class Dumper(DumperBase): ...@@ -1621,27 +1675,22 @@ class Dumper(DumperBase):
self.reportError(error) self.reportError(error)
self.reportVariables() self.reportVariables()
def convertHash(args): def createResolvePendingBreakpointsHookBreakpoint(self, fullName, lineNumber):
if sys.version_info[0] == 3: self.nativeMixed = True
return args if self.qmlTriggeredBreakpoint is None:
if isinstance(args, str): self.qmlTriggeredBreakpoint = \
return args self.target.BreakpointCreateByName("qt_v4TriggeredBreakpointHook")
if isinstance(args, unicode):
return args.encode('utf8') bp = self.target.BreakpointCreateByName("qt_v4ResolvePendingBreakpointsHook")
cargs = {} bp.SetOneShot(True)
for arg in args: self.qmlBreakpointResolvers[bp] = lambda: \
rhs = args[arg] self.resolvePendingQmlBreakpoint(fullName, lineNumber)
if type(rhs) == type([]):
rhs = [convertHash(i) for i in rhs] def resolvePendingQmlBreakpoint(self, fullName, lineNumber):
elif type(rhs) == type({}): bp = self.doInsertQmlBreakPoint(fullName, lineNumber)
rhs = convertHash(rhs) print("Resolving QML breakpoint %s:%s -> %s" % (fullName, lineNumber, bp))
else:
try: #ns = self.qtNamespace()
rhs = rhs.encode('utf8')
except:
pass
cargs[arg.encode('utf8')] = rhs
return cargs
# Used in dumper auto test. # Used in dumper auto test.
......
...@@ -574,8 +574,8 @@ void LldbEngine::insertBreakpointHelper(DebuggerCommand *cmd, Breakpoint bp) con ...@@ -574,8 +574,8 @@ void LldbEngine::insertBreakpointHelper(DebuggerCommand *cmd, Breakpoint bp) con
cmd->arg("function", bp.functionName().toUtf8()); cmd->arg("function", bp.functionName().toUtf8());
cmd->arg("oneshot", bp.isOneShot()); cmd->arg("oneshot", bp.isOneShot());
cmd->arg("enabled", bp.isEnabled()); cmd->arg("enabled", bp.isEnabled());
cmd->arg("file", bp.fileName().toUtf8()); cmd->arg("fileName", bp.fileName().toUtf8());
cmd->arg("line", bp.lineNumber()); cmd->arg("lineNumber", bp.lineNumber());
cmd->arg("address", bp.address()); cmd->arg("address", bp.address());
cmd->arg("expression", bp.expression()); cmd->arg("expression", bp.expression());
bp.notifyBreakpointInsertProceeding(); bp.notifyBreakpointInsertProceeding();
...@@ -593,8 +593,8 @@ void LldbEngine::changeBreakpoint(Breakpoint bp) ...@@ -593,8 +593,8 @@ void LldbEngine::changeBreakpoint(Breakpoint bp)
cmd.arg("function", bp.functionName().toUtf8()); cmd.arg("function", bp.functionName().toUtf8());
cmd.arg("oneshot", bp.isOneShot()); cmd.arg("oneshot", bp.isOneShot());
cmd.arg("enabled", bp.isEnabled()); cmd.arg("enabled", bp.isEnabled());
cmd.arg("file", bp.fileName().toUtf8()); cmd.arg("fileName", bp.fileName().toUtf8());
cmd.arg("line", bp.lineNumber()); cmd.arg("lineNumber", bp.lineNumber());
cmd.arg("address", bp.address()); cmd.arg("address", bp.address());
cmd.arg("expression", bp.expression()); cmd.arg("expression", bp.expression());
bp.notifyBreakpointChangeProceeding(); bp.notifyBreakpointChangeProceeding();
...@@ -1020,7 +1020,11 @@ void LldbEngine::refreshStack(const GdbMi &stack) ...@@ -1020,7 +1020,11 @@ void LldbEngine::refreshStack(const GdbMi &stack)
frame.from = item["func"].toUtf8(); frame.from = item["func"].toUtf8();
frame.line = item["line"].toInt(); frame.line = item["line"].toInt();
frame.address = item["addr"].toAddress(); frame.address = item["addr"].toAddress();
frame.usable = QFileInfo(frame.file).isReadable(); GdbMi usable = item["usable"];
if (usable.isValid())
frame.usable = usable.data().toInt();
else
frame.usable = QFileInfo(frame.file).isReadable();
frames.append(frame); frames.append(frame);
} }
bool canExpand = stack["hasmore"].toInt(); bool canExpand = stack["hasmore"].toInt();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment