diff --git a/share/qtcreator/dumper/lbridge.py b/share/qtcreator/dumper/lbridge.py index c2ebc24e6320ad82e6ec81f0330604825f68a200..f3cdc691406fff283f1ca8ac22851d3622d193be 100755 --- a/share/qtcreator/dumper/lbridge.py +++ b/share/qtcreator/dumper/lbridge.py @@ -20,14 +20,110 @@ cdbLoaded = False lldbLoaded = False gdbLoaded = False +# Encodings. Keep that synchronized with DebuggerEncoding in watchutils.h +Unencoded8Bit, \ +Base64Encoded8BitWithQuotes, \ +Base64Encoded16BitWithQuotes, \ +Base64Encoded32BitWithQuotes, \ +Base64Encoded16Bit, \ +Base64Encoded8Bit, \ +Hex2EncodedLatin1, \ +Hex4EncodedLittleEndian, \ +Hex8EncodedLittleEndian, \ +Hex2EncodedUtf8, \ +Hex8EncodedBigEndian, \ +Hex4EncodedBigEndian, \ +Hex4EncodedLittleEndianWithoutQuotes, \ +Hex2EncodedLocal8Bit, \ +JulianDate, \ +MillisecondsSinceMidnight, \ +JulianDateAndMillisecondsSinceMidnight, \ +Hex2EncodedInt1, \ +Hex2EncodedInt2, \ +Hex2EncodedInt4, \ +Hex2EncodedInt8, \ +Hex2EncodedUInt1, \ +Hex2EncodedUInt2, \ +Hex2EncodedUInt4, \ +Hex2EncodedUInt8, \ +Hex2EncodedFloat4, \ +Hex2EncodedFloat8 \ + = range(27) + +# Display modes. Keep that synchronized with DebuggerDisplay in watchutils.h +StopDisplay, \ +DisplayImageData, \ +DisplayUtf16String, \ +DisplayImageFile, \ +DisplayProcess, \ +DisplayLatin1String, \ +DisplayUtf8String \ + = range(7) + ####################################################################### # # Helpers # ####################################################################### -currentDir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) -#print "DIR: %s " % currentDir +# This is a cache mapping from 'type name' to 'display alternatives'. +qqFormats = {} + +# This is a cache of all known dumpers. +qqDumpers = {} + +# This is a cache of all dumpers that support writing. +qqEditable = {} + +# This keeps canonical forms of the typenames, without array indices etc. +qqStripForFormat = {} + +def stripForFormat(typeName): + global qqStripForFormat + if typeName in qqStripForFormat: + return qqStripForFormat[typeName] + stripped = "" + inArray = 0 + for c in stripClassTag(typeName): + if c == '<': + break + if c == ' ': + continue + if c == '[': + inArray += 1 + elif c == ']': + inArray -= 1 + if inArray and ord(c) >= 48 and ord(c) <= 57: + continue + stripped += c + qqStripForFormat[typeName] = stripped + return stripped + + +def registerDumper(function): + global qqDumpers, qqFormats, qqEditable + try: + funcname = function.func_name + if funcname.startswith("qdump__"): + type = funcname[7:] + qqDumpers[type] = function + qqFormats[type] = qqFormats.get(type, "") + elif funcname.startswith("qform__"): + type = funcname[7:] + formats = "" + try: + formats = function() + except: + pass + qqFormats[type] = formats + elif funcname.startswith("qedit__"): + type = funcname[7:] + try: + qqEditable[type] = function + except: + pass + except: + pass def warn(message): print 'XXX={"%s"}@\n' % message.encode("latin1").replace('"', "'") @@ -68,22 +164,6 @@ class Type: def strip_typedefs(self): return self -class Value: - def __init__(self, var): - self.raw = var - self.is_optimized_out = False - self.address = var.addr - self.type = Type(var) - self.name = var.name - - def __str__(self): - return str(self.raw.value) - - def __getitem__(self, name): - return None if self.raw is None else self.raw.GetChildMemberWithName(name) - - def fields(self): - return [Value(self.raw.GetChildAtIndex(i)) for i in xrange(self.raw.num_children)] def currentFrame(): currentThread = self.process.GetThreadAtIndex(0) @@ -167,6 +247,33 @@ def loggingCallback(args): s = s.replace('"', "'") sys.stdout.write('log="%s"@\n' % s) +def check(exp): + if not exp: + raise RuntimeError("Check failed") + +def checkSimpleRef(ref): + count = ref["_q_value"] + check(count > 0) + check(count < 1000000) + +def checkRef(ref): + return True + try: + count = ref["atomic"]["_q_value"] # Qt 5. + minimum = -1 + except: + count = ref["_q_value"] # Qt 4. + minimum = 0 + # Assume there aren't a million references to any object. + check(count >= minimum) + check(count < 1000000) + + +def valueChildAccess(self, name): + return self.GetChildMemberWithName(name) + +lldb.SBValue.__getitem__ = valueChildAccess + class Children: def __init__(self, d, numChild = 1, childType = None, childNumChild = None, maxNumChild = None, addrBase = None, addrStep = None): @@ -272,10 +379,17 @@ class Debugger: def put(self, stuff): sys.stdout.write(stuff) + def currentItemFormat(self): + #format = self.formats.get(self.currentIName) + #if format is None: + # format = self.typeformats.get(stripForFormat(str(self.currentType))) + #return format + return 0 + def putNumChild(self, numchild): #warn("NUM CHILD: '%s' '%s'" % (numchild, self.currentChildNumChild)) - if numchild != self.currentChildNumChild: - self.put('numchild="%s",' % numchild) + #if numchild != self.currentChildNumChild: + self.put('numchild="%s",' % numchild) def putValue(self, value, encoding = None, priority = 0): # Higher priority values override lower ones. @@ -283,6 +397,7 @@ class Debugger: self.currentValue = value self.currentValuePriority = priority self.currentValueEncoding = encoding + #self.put('value="%s",' % value) def setupInferior(self, args): fileName = args['executable'] @@ -293,6 +408,7 @@ class Debugger: self.report('state="inferiorsetupok",msg="%s",exe="%s"' % (error, fileName)) else: self.report('state="inferiorsetupfailed",msg="%s",exe="%s"' % (error, fileName)) + self.importDumpers() def runEngine(self, _): error = lldb.SBError() @@ -364,7 +480,7 @@ class Debugger: result += '],current-thread-id="%s"},' % self.currentThread().id self.report(result) - def reportStack(self): + def reportStack(self, _ = None): if self.process is None: self.report('msg="No process"') else: @@ -386,32 +502,89 @@ class Debugger: result += '],hasmore="%s"},' % hasmore self.report(result) + def putType(self, typeName): + self.put('type="%s",' % typeName) + + def putStringValue(self, value, priority = 0): + if not value is None: + str = self.encodeString(value) + self.putValue(str, Hex4EncodedLittleEndian, priority) + + def readRawMemory(self, base, size): + error = lldb.SBError() + contents = self.process.ReadMemory(base, size, error) + return binascii.hexlify(contents) + + def computeLimit(self, size, limit): + if limit is None: + return size + if limit == 0: + #return min(size, qqStringCutOff) + return min(size, 100) + return min(size, limit) + + def qStringData(self, value): + private = value['d'] + checkRef(private['ref']) + try: + # Qt 5. Will fail on Qt 4 due to the missing 'offset' member. + offset = private['offset'] + data = int(private.GetValue(), 0) + int(offset.GetValue()) + return data, int(private['size'].GetValue()), int(private['alloc'].GetValue()) + except: + # Qt 4. + return private['data'], int(private['size']), int(private['alloc']) + + def encodeString(self, value, limit = 0): + data, size, alloc = self.qStringData(value) + if alloc != 0: + check(0 <= size and size <= alloc and alloc <= 100*1000*1000) + limit = self.computeLimit(size, limit) + s = self.readRawMemory(data, 2 * limit) + if limit < size: + s += "2e002e002e00" + return s + + def putValue(self, value, encoding = None, priority = 0): + # Higher priority values override lower ones. + #if priority >= self.currentValuePriority: + # self.currentValue = value + # self.currentValuePriority = priority + # self.currentValueEncoding = encoding + if not encoding is None: + self.put('valueencoded="%d",' % encoding) + self.put('value="%s",' % value) + def putItem(self, value, tryDynamic=True): - #val = value.GetDynamicValue(lldb.eDynamicCanRunTarget) - val = value - v = val.GetValue() - #numchild = 1 if val.MightHaveChildren() else 0 - numchild = val.GetNumChildren() - self.put('iname="%s",' % self.currentIName) - self.put('type="%s",' % val.GetTypeName()) - #self.put('vtype="%s",' % val.GetValueType()) - self.put('value="%s",' % ("" if v is None else v)) - self.put('numchild="%s",' % numchild) - self.put('addr="0x%x",' % val.GetLoadAddress()) - if self.currentIName in self.expandedINames: - with Children(self): - self.putFields(value) + global qqDumpers + #value = value.GetDynamicValue(lldb.eDynamicCanRunTarget) + typeName = value.GetTypeName() + if typeName in qqDumpers: + self.putType(typeName) + qqDumpers[typeName](self, value) + else: + v = value.GetValue() + #numchild = 1 if value.MightHaveChildren() else 0 + numchild = value.GetNumChildren() + self.put('iname="%s",' % self.currentIName) + self.put('type="%s",' % typeName) + self.put('value="%s",' % ("" if v is None else v)) + self.put('numchild="%s",' % numchild) + self.put('addr="0x%x",' % value.GetLoadAddress()) + if self.currentIName in self.expandedINames: + with Children(self): + self.putFields(value) def putFields(self, value): n = value.GetNumChildren() if n > 10000: n = 10000 for i in xrange(n): - field = value.GetChildAtIndex(i) - with SubItem(self, field.GetName()): - self.putItem(field) + child = value.GetChildAtIndex(i) + with SubItem(self, child.GetName()): + self.putItem(child) - def reportVariables(self): + def reportVariables(self, _ = None): frame = self.currentThread().GetSelectedFrame() self.currentIName = "local" self.put('data=[') @@ -421,7 +594,7 @@ class Debugger: self.put(']') self.report('') - def reportData(self): + def reportData(self, _ = None): self.reportRegisters() if self.process is None: self.report('process="none"') @@ -451,7 +624,7 @@ class Debugger: sys.stdout.write(stuff) sys.stdout.write("@\n") - def interruptInferior(self): + def interruptInferior(self, _ = None): if self.process is None: self.report('msg="No process"') else: @@ -459,7 +632,7 @@ class Debugger: error = self.process.Stop() self.reportError(error) - def detachInferior(self): + def detachInferior(self, _ = None): if self.process is None: self.report('msg="No process"') else: @@ -467,7 +640,7 @@ class Debugger: self.reportError(error) self.reportData() - def continueInferior(self): + def continueInferior(self, _ = None): if self.process is None: self.report('msg="No process"') else: @@ -585,7 +758,7 @@ class Debugger: result += "]" self.report(result) - def do_listModules(self, args): + def listModules(self, args): result = 'modules=[' for module in self.target.modules: result += '{file="%s"' % module.file.fullpath @@ -667,8 +840,9 @@ class Debugger: def requestModuleSymbols(self, frame): self.handleCommand("target module list " + frame) - def executeDebuggerCommand(self, command): + def executeDebuggerCommand(self, args): result = lldb.SBCommandReturnObject() + command = args['command'] self.debugger.GetCommandInterpreter().HandleCommand(command, result) success = result.Succeeded() if success: @@ -715,21 +889,19 @@ class Debugger: result += ',contents="%s"}' % binascii.hexlify(contents) self.report(result) -#execfile(os.path.join(currentDir, "dumper.py")) -#execfile(os.path.join(currentDir, "qttypes.py")) - -#def importPlainDumpers(args): -# pass + def importDumpers(self, _ = None): + result = lldb.SBCommandReturnObject() + interpreter = self.debugger.GetCommandInterpreter() + global qqDumpers, qqFormats, qqEditable + items = globals() + for key in items: + registerDumper(items[key]) -#def bbsetup(args = ''): -# global qqDumpers, qqFormats, qqEditable -# typeCache = {} -# -# items = globals() -# for key in items: -# registerDumper(items[key]) -#bbsetup() +currentDir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +#warn("currentdir: %s" % currentDir) +#execfile(os.path.join(currentDir, "dumper.py")) +execfile(os.path.join(currentDir, "qttypes.py")) import sys import select @@ -739,6 +911,7 @@ lldbLoaded = True if __name__ == '__main__': db = Debugger() db.report('state="enginesetupok"') + #importPlainDumpers() while True: rlist, _, _ = select.select([sys.stdin], [], [], 0.1) if rlist: diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 25887ec863eca33a5dc762b4c7a569c08625a560..457320524cd46335967b1832a44f36332118de61 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -82,34 +82,26 @@ LldbEngine::LldbEngine(const DebuggerStartParameters &startParameters) : DebuggerEngine(startParameters) { m_lastAgentId = 0; + m_lastToken = 0; setObjectName(QLatin1String("LldbEngine")); } LldbEngine::~LldbEngine() {} -void LldbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages) +void LldbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages) { - if (!(languages & CppLanguage)) - return; - QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); - if (state() == DebuggerNotReady) { - showMessage(_("LLDB PROCESS NOT RUNNING, PLAIN CMD IGNORED: ") + command); - return; - } - //runCommand(command.toUtf8()); - m_lldbProc.write(command.toUtf8() + '\n'); + runCommand(Command("executeDebuggerCommand").arg("command", command)); } -static int token = 1; - void LldbEngine::runCommand(const Command &command) { QTC_ASSERT(m_lldbProc.state() == QProcess::Running, notifyEngineIll()); - ++token; + ++m_lastToken; + QByteArray token = QByteArray::number(m_lastToken); QByteArray cmd = "db {'cmd':'" + command.function + "'," - + command.args + "'token':" + QByteArray::number(token) + "}\n"; - showMessage(QString::number(token) + _(cmd), LogInput); + + command.args + "'token':" + token + "}\n"; + showMessage(_(token + cmd), LogInput); m_lldbProc.write(cmd); } diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index 6679c56b775ae40e7d1b7f68ba116050de994533..4b37429130747df537d6ff70ef2d7249d11a1bbe 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -185,6 +185,7 @@ private: // FIXME: Make generic. int m_lastAgentId; + int m_lastToken; QMap<QPointer<DisassemblerAgent>, int> m_disassemblerAgents; QMap<QPointer<MemoryAgent>, int> m_memoryAgents; QHash<int, QPointer<QObject> > m_memoryAgentTokens;