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:
expr = 'qt_v4DebuggerHook("{%s}")' % data
try:
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:
#print("QML command failed: %s: %s" % (expr, error))
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
def prepareQmlStep(self, _):
......@@ -1765,6 +1769,16 @@ class DumperBase:
print("Resolving QML breakpoint: %s" % 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"
......
......@@ -1752,14 +1752,6 @@ class Dumper(DumperBase):
'fullname="%s",line="%s",language="js",addr="0x%x"}')
% (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):
self.prepare("options:" + options + ",pe")
self.output = []
......
......@@ -256,6 +256,9 @@ class Dumper(DumperBase):
self.isInterrupting_ = False
self.dummyValue = None
self.breakpointsToCheck = set([])
self.qmlBreakpointResolvers = {}
self.qmlTriggeredBreakpoint = None
self.nativeMixed = False
def enterSubItem(self, item):
if isinstance(item.name, lldb.SBValue):
......@@ -809,15 +812,41 @@ class Dumper(DumperBase):
if not frame.IsValid():
isLimited = False
break
lineEntry = frame.GetLineEntry()
line = lineEntry.GetLine()
result += '{pc="0x%x"' % frame.GetPC()
result += ',level="%d"' % frame.idx
result += ',addr="0x%x"' % frame.GetPCAddress().GetLoadAddress(self.target)
result += ',func="%s"' % frame.GetFunctionName()
result += ',line="%d"' % line
result += ',fullname="%s"' % fileName(lineEntry.file)
result += ',file="%s"},' % fileName(lineEntry.file)
lineNumber = lineEntry.GetLine()
pc = frame.GetPC()
level = frame.idx
addr = frame.GetPCAddress().GetLoadAddress(self.target)
functionName = frame.GetFunctionName()
fullname = 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 += ',hasmore="%d"' % isLimited
result += ',limit="%d"' % limit
......@@ -1216,8 +1245,9 @@ class Dumper(DumperBase):
msg = lldb.SBEvent.GetCStringFromEvent(event)
flavor = event.GetDataFlavor()
state = lldb.SBProcess.GetStateFromEvent(event)
self.report('event={type="%s",data="%s",msg="%s",flavor="%s",state="%s"}'
% (eventType, out.GetData(), msg, flavor, state))
bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
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:
self.eventState = state
if state == lldb.eStateExited:
......@@ -1228,12 +1258,32 @@ class Dumper(DumperBase):
self.report('exited={status="%s",desc="%s"}'
% (self.process.GetExitStatus(), self.process.GetExitDescription()))
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_:
self.isInterrupting_ = False
self.reportState("inferiorstopok")
elif self.ignoreStops > 0:
self.ignoreStops -= 1
self.process.Continue()
#elif bp and bp in self.qmlBreakpointResolvers:
# self.report("RESOLVER HIT")
# self.qmlBreakpointResolvers[bp]()
# self.process.Continue();
else:
self.reportState("stopped")
else:
......@@ -1311,20 +1361,24 @@ class Dumper(DumperBase):
"main", self.target.GetExecutable().GetFilename())
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
modelId = args['modelid']
bpType = args["type"]
if bpType == BreakpointByFileAndLine:
bp = self.target.BreakpointCreateByLocation(
str(args["file"]), int(args["line"]))
str(args["fileName"]), int(args["lineNumber"]))
elif bpType == BreakpointByFunction:
bp = self.target.BreakpointCreateByName(args["function"])
elif bpType == BreakpointByAddress:
bp = self.target.BreakpointCreateByAddress(args["address"])
elif bpType == BreakpointAtMain:
bp = self.createBreakpointAtMain()
elif bpType == BreakpointByFunction:
bp = self.target.BreakpointCreateByName(args["function"])
elif bpType == BreakpointAtThrow:
bp = self.target.BreakpointCreateForException(
lldb.eLanguageTypeC_plus_plus, False, True)
......@@ -1621,27 +1675,22 @@ class Dumper(DumperBase):
self.reportError(error)
self.reportVariables()
def convertHash(args):
if sys.version_info[0] == 3:
return args
if isinstance(args, str):
return args
if isinstance(args, unicode):
return args.encode('utf8')
cargs = {}
for arg in args:
rhs = args[arg]
if type(rhs) == type([]):
rhs = [convertHash(i) for i in rhs]
elif type(rhs) == type({}):
rhs = convertHash(rhs)
else:
try:
rhs = rhs.encode('utf8')
except:
pass
cargs[arg.encode('utf8')] = rhs
return cargs
def createResolvePendingBreakpointsHookBreakpoint(self, fullName, lineNumber):
self.nativeMixed = True
if self.qmlTriggeredBreakpoint is None:
self.qmlTriggeredBreakpoint = \
self.target.BreakpointCreateByName("qt_v4TriggeredBreakpointHook")
bp = self.target.BreakpointCreateByName("qt_v4ResolvePendingBreakpointsHook")
bp.SetOneShot(True)
self.qmlBreakpointResolvers[bp] = lambda: \
self.resolvePendingQmlBreakpoint(fullName, lineNumber)
def resolvePendingQmlBreakpoint(self, fullName, lineNumber):
bp = self.doInsertQmlBreakPoint(fullName, lineNumber)
print("Resolving QML breakpoint %s:%s -> %s" % (fullName, lineNumber, bp))
#ns = self.qtNamespace()
# Used in dumper auto test.
......
......@@ -574,8 +574,8 @@ void LldbEngine::insertBreakpointHelper(DebuggerCommand *cmd, Breakpoint bp) con
cmd->arg("function", bp.functionName().toUtf8());
cmd->arg("oneshot", bp.isOneShot());
cmd->arg("enabled", bp.isEnabled());
cmd->arg("file", bp.fileName().toUtf8());
cmd->arg("line", bp.lineNumber());
cmd->arg("fileName", bp.fileName().toUtf8());
cmd->arg("lineNumber", bp.lineNumber());
cmd->arg("address", bp.address());
cmd->arg("expression", bp.expression());
bp.notifyBreakpointInsertProceeding();
......@@ -593,8 +593,8 @@ void LldbEngine::changeBreakpoint(Breakpoint bp)
cmd.arg("function", bp.functionName().toUtf8());
cmd.arg("oneshot", bp.isOneShot());
cmd.arg("enabled", bp.isEnabled());
cmd.arg("file", bp.fileName().toUtf8());
cmd.arg("line", bp.lineNumber());
cmd.arg("fileName", bp.fileName().toUtf8());
cmd.arg("lineNumber", bp.lineNumber());
cmd.arg("address", bp.address());
cmd.arg("expression", bp.expression());
bp.notifyBreakpointChangeProceeding();
......@@ -1020,7 +1020,11 @@ void LldbEngine::refreshStack(const GdbMi &stack)
frame.from = item["func"].toUtf8();
frame.line = item["line"].toInt();
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);
}
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