diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 80e022f5215..c51e926a685 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -71,6 +71,19 @@ except: return "Normal" +class ReportItem: + """ + Helper structure to keep temporary "best" information about a value + or a type scheduled to be reported. This might get overridden be + subsequent better guesses during a putItem() run. + """ + def __init__(self): + self.value = None + self.priority = -100 + self.encoding = None + self.elided = 0 + + class Blob(object): """ Helper structure to keep a blob of bytes, possibly @@ -310,6 +323,7 @@ class DumperBase: # Later set, or not set: # cachedQtVersion self.stringCutOff = 10000 + self.displayStringLimit = 100 # This is a cache mapping from 'type name' to 'display alternatives'. self.qqFormats = {} @@ -375,12 +389,11 @@ class DumperBase: # assume no Qt 3 support by default return False + # Clamps size to limit. def computeLimit(self, size, limit): - if limit is None: - return size - if limit == 0: - return min(size, self.stringCutOff) - return min(size, limit) + if limit is None or size <= limit: + return 0, size + return size, limit def vectorDataHelper(self, addr): if self.qtVersion() >= 0x050000: @@ -419,53 +432,50 @@ class DumperBase: return data, size, alloc # addr is the begin of a QByteArrayData structure - def encodeStringHelper(self, addr, limit = 0): + def encodeStringHelper(self, addr, limit): # Should not happen, but we get it with LLDB as result # of inferior calls if addr == 0: - return "" + return 0, "" data, size, alloc = self.byteArrayDataHelper(addr) if alloc != 0: self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000) - limit = self.computeLimit(size, limit) - s = self.readMemory(data, 2 * limit) - if limit < size: - s += "2e002e002e00" - return s + elided, shown = self.computeLimit(size, limit) + return elided, self.readMemory(data, 2 * shown) - def encodeByteArrayHelper(self, addr, limit = None): + def encodeByteArrayHelper(self, addr, limit): data, size, alloc = self.byteArrayDataHelper(addr) if alloc != 0: self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000) - limit = self.computeLimit(size, limit) - s = self.readMemory(data, limit) - if limit < size: - s += "2e2e2e" - return s + elided, shown = self.computeLimit(size, limit) + return elided, self.readMemory(data, shown) def readMemory(self, addr, size): data = self.extractBlob(addr, size).toBytes() return self.hexencode(data) def encodeByteArray(self, value, limit = 0): - return self.encodeByteArrayHelper(self.extractPointer(value), limit) + elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit) + return data def byteArrayData(self, value): return self.byteArrayDataHelper(self.extractPointer(value)) def putByteArrayValue(self, value): - return self.putValue(self.encodeByteArray(value, self.stringCutOff), Hex2EncodedLatin1) + elided, data = self.encodeByteArrayHelper(self.extractPointer(value), self.displayStringLimit) + self.putValue(data, Hex2EncodedLatin1, elided=elided) def putByteArrayValueByAddress(self, addr): - self.putValue(self.encodeByteArrayHelper(self.extractPointer(addr)), - Hex2EncodedLatin1) + elided, data = self.encodeByteArrayHelper(addr, self.displayStringLimit) + self.putValue(data, Hex2EncodedLatin1, elided=elided) def putStringValueByAddress(self, addr): - self.putValue(self.encodeStringHelper(self.extractPointer(addr)), - Hex4EncodedLittleEndian) + elided, data = self.encodeStringHelper(self.extractPointer(addr), self.displayStringLimit) + self.putValue(data, Hex4EncodedLittleEndian, elided=elided) def encodeString(self, value, limit = 0): - return self.encodeStringHelper(self.extractPointer(value), limit) + elided, data = self.encodeStringHelper(self.extractPointer(value), limit) + return data def stringData(self, value): return self.byteArrayDataHelper(self.extractPointer(value)) @@ -499,7 +509,8 @@ class DumperBase: return inner.strip() def putStringValue(self, value): - return self.putValue(self.encodeString(value, self.stringCutOff), Hex4EncodedLittleEndian) + elided, data = self.encodeStringHelper(self.extractPointer(value), self.displayStringLimit) + self.putValue(data, Hex4EncodedLittleEndian, elided=elided) def putAddressItem(self, name, value, type = ""): with SubItem(self, name): @@ -619,27 +630,16 @@ class DumperBase: def findFirstZero(self, p, maximum): for i in xrange(maximum): if int(p.dereference()) == 0: - return i + return 0, i p = p + 1 - return maximum + 1 + # Real end is unknown. + return -1, maximum - def encodeCArray(self, p, innerType, suffix): + def encodeCArray(self, p, innerType, limit): t = self.lookupType(innerType) p = p.cast(t.pointer()) - limit = self.findFirstZero(p, self.stringCutOff) - s = self.readMemory(p, limit * t.sizeof) - if limit > self.stringCutOff: - s += suffix - return s - - def encodeCharArray(self, p): - return self.encodeCArray(p, "unsigned char", "2e2e2e") - - def encodeChar2Array(self, p): - return self.encodeCArray(p, "unsigned short", "2e002e002e00") - - def encodeChar4Array(self, p): - return self.encodeCArray(p, "unsigned int", "2e0000002e0000002e000000") + elided, shown = self.findFirstZero(p, limit) + return elided, self.readMemory(p, shown * t.sizeof) def putItemCount(self, count, maximum = 1000000000): # This needs to override the default value, so don't use 'put' directly. @@ -654,26 +654,34 @@ class DumperBase: def putType(self, type, priority = 0): # Higher priority values override lower ones. - if priority >= self.currentTypePriority: - self.currentType = str(type) - self.currentTypePriority = priority + if priority >= self.currentType.priority: + self.currentType.value = str(type) + self.currentType.priority = priority - def putValue(self, value, encoding = None, priority = 0): + def putValue(self, value, encoding = None, priority = 0, elided = None): # Higher priority values override lower ones. - if priority >= self.currentValuePriority: - self.currentValue = value - self.currentValuePriority = priority - self.currentValueEncoding = encoding + # elided = 0 indicates all data is available in value, + # otherwise it's the true length. + if priority >= self.currentValue.priority: + self.currentValue.value = value + self.currentValue.priority = priority + self.currentValue.encoding = encoding + self.currentValue.elided = elided def putEmptyValue(self, priority = -10): - if priority >= self.currentValuePriority: - self.currentValue = "" - self.currentValuePriority = priority - self.currentValueEncoding = None + if priority >= self.currentValue.priority: + self.currentValue.value = "" + self.currentValue.priority = priority + self.currentValue.encoding = None + self.currentValue.elided = None def putName(self, name): self.put('name="%s",' % name) + def putBetterType(self, type): + self.currentType.value = str(type) + self.currentType.priority += 1 + def putNoType(self): # FIXME: replace with something that does not need special handling # in SubItem.__exit__(). @@ -682,7 +690,7 @@ class DumperBase: def putInaccessible(self): #self.putBetterType(" ") self.putNumChild(0) - self.currentValue = None + self.currentValue.value = None def putNamedSubItem(self, component, value, name): with SubItem(self, component): @@ -805,7 +813,8 @@ class DumperBase: if format == None and innerTypeName == "char": # Use Latin1 as default for char *. self.putType(typeName) - self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1) + (elided, data) = self.encodeCArray(value, "unsigned char", self.displayStringLimit) + self.putValue(data, Hex2EncodedLatin1, elided=elided) self.putNumChild(0) return @@ -823,35 +832,40 @@ class DumperBase: if format == Latin1StringFormat: # Explicitly requested Latin1 formatting. self.putType(typeName) - self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1) + (elided, data) = self.encodeCArray(value, "unsigned char", self.displayStringLimit) + self.putValue(data, Hex2EncodedLatin1, elided=elided) self.putNumChild(0) return if format == Utf8StringFormat: # Explicitly requested UTF-8 formatting. self.putType(typeName) - self.putValue(self.encodeCharArray(value), Hex2EncodedUtf8) + (elided, data) = self.encodeCArray(value, "unsigned char", self.displayStringLimit) + self.putValue(data, Hex2EncodedUtf8, elided=elided) self.putNumChild(0) return if format == Local8BitStringFormat: # Explicitly requested local 8 bit formatting. self.putType(typeName) - self.putValue(self.encodeCharArray(value), Hex2EncodedLocal8Bit) + (elided, data) = self.encodeCArray(value, "unsigned char", self.displayStringLimit) + self.putValue(data, Hex2EncodedLocal8Bit, elided=elided) self.putNumChild(0) return if format == Utf16StringFormat: # Explicitly requested UTF-16 formatting. self.putType(typeName) - self.putValue(self.encodeChar2Array(value), Hex4EncodedLittleEndian) + (elided, data) = self.encodeCArray(value, "unsigned short", self.displayStringLimit) + self.putValue(data, Hex4EncodedLittleEndian, elided=elided) self.putNumChild(0) return if format == Ucs4StringFormat: # Explicitly requested UCS-4 formatting. self.putType(typeName) - self.putValue(self.encodeChar4Array(value), Hex8EncodedLittleEndian) + (elided, data) = self.encodeCArray(value, "unsigned int", self.displayStringLimit) + self.putValue(data, Hex8EncodedLittleEndian, elided=elided) self.putNumChild(0) return @@ -1226,7 +1240,7 @@ class DumperBase: format = self.formats.get(self.currentIName) if format is None: if type is None: - type = self.currentType + type = self.currentType.value needle = self.stripForFormat(str(type)) format = self.typeformats.get(needle) return format @@ -1256,8 +1270,8 @@ class DumperBase: if not hasPlot(): return if not self.isSimpleType(typeobj): - #self.putValue(self.currentValue + " (not plottable)") - self.putValue(self.currentValue) + #self.putValue(self.currentValue.value + " (not plottable)") + self.putValue(self.currentValue.value) self.putField("plottable", "0") return global gnuplotPipe @@ -1435,8 +1449,8 @@ class DumperBase: value = self.parseAndEvaluate(exp) self.putItem(value) except RuntimeError: - self.currentType = " " - self.currentValue = "" + self.currentType.value = " " + self.currentValue.value = "" self.currentChildNumChild = -1 self.currentNumChild = 0 self.putNumChild(0) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 122c50bd9db..7c228cf058b 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -367,11 +367,8 @@ class Dumper(DumperBase): self.currentChildNumChild = -1 self.currentMaxNumChild = -1 self.currentNumChild = -1 - self.currentValue = None - self.currentValuePriority = -100 - self.currentValueEncoding = None - self.currentType = None - self.currentTypePriority = -100 + self.currentValue = ReportItem() + self.currentType = ReportItem() self.currentAddress = None self.typeformats = {} self.formats = {} @@ -402,6 +399,8 @@ class Dumper(DumperBase): self.expandedINames = set(arg[pos:].split(",")) elif arg.startswith("stringcutoff:"): self.stringCutOff = int(arg[pos:]) + elif arg.startswith("displaystringlimit:"): + self.displayStringLimit = int(arg[pos:]) elif arg.startswith("typeformats:"): for f in arg[pos:].split(","): pos = f.find("=") @@ -525,39 +524,39 @@ class Dumper(DumperBase): self.put('name="%s",' % item.name) item.savedIName = self.currentIName item.savedValue = self.currentValue - item.savedValuePriority = self.currentValuePriority - item.savedValueEncoding = self.currentValueEncoding item.savedType = self.currentType - item.savedTypePriority = self.currentTypePriority item.savedCurrentAddress = self.currentAddress self.currentIName = item.iname - self.currentValuePriority = -100 - self.currentValueEncoding = None - self.currentType = "" - self.currentTypePriority = -100 + self.currentValue = ReportItem(); + self.currentType = ReportItem(); self.currentAddress = None def exitSubItem(self, item, exType, exValue, exTraceBack): - #warn(" CURRENT VALUE: %s %s %s" % (self.currentValue, - # self.currentValueEncoding, self.currentValuePriority)) + #warn("CURRENT VALUE: %s: %s %s %s %s" % ( + # self.currentIName, + # self.currentValue.value, + # self.currentValue.elided, + # self.currentValue.encoding, + # self.currentValue.priority)) if not exType is None: if self.passExceptions: showException("SUBITEM", exType, exValue, exTraceBack) self.putNumChild(0) self.putValue("") try: - #warn("TYPE VALUE: %s" % self.currentValue) - typeName = stripClassTag(self.currentType) - #warn("TYPE: '%s' DEFAULT: '%s' % (typeName, self.currentChildType)) + #warn("CURRENT TYPE: %s" % self.currentType.value) + typeName = stripClassTag(self.currentType.value) if len(typeName) > 0 and typeName != self.currentChildType: self.put('type="%s",' % typeName) # str(type.unqualified()) ? - if self.currentValue is None: + if self.currentValue.value is None: self.put('value="",numchild="0",') else: - if not self.currentValueEncoding is None: - self.put('valueencoded="%d",' % self.currentValueEncoding) - self.put('value="%s",' % self.currentValue) + if not self.currentValue.encoding is None: + self.put('valueencoded="%d",' % self.currentValue.encoding) + if self.currentValue.elided: + self.put('valueelided="%d",' % self.currentValue.elided) + self.put('value="%s",' % self.currentValue.value) except: pass if not self.currentAddress is None: @@ -565,10 +564,7 @@ class Dumper(DumperBase): self.put('},') self.currentIName = item.savedIName self.currentValue = item.savedValue - self.currentValuePriority = item.savedValuePriority - self.currentValueEncoding = item.savedValueEncoding self.currentType = item.savedType - self.currentTypePriority = item.savedTypePriority self.currentAddress = item.savedCurrentAddress return True @@ -888,10 +884,6 @@ class Dumper(DumperBase): self.isQt3Support = lambda: self.cachedIsQt3Suport return self.cachedIsQt3Suport - def putBetterType(self, type): - self.currentType = str(type) - self.currentTypePriority = self.currentTypePriority + 1 - def putAddress(self, addr): if self.currentPrintsAddress: try: diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index ec03130c066..5ccf12695ca 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -261,11 +261,8 @@ class Dumper(DumperBase): self.typeformats = {} self.currentIName = None - self.currentValuePriority = -100 - self.currentValueEncoding = None - self.currentType = "" - self.currentTypePriority = -100 - self.currentValue = None + self.currentValue = ReportItem() + self.currentType = ReportItem() self.currentNumChild = None self.currentMaxNumChild = None self.currentPrintsAddress = None @@ -310,15 +307,10 @@ class Dumper(DumperBase): self.put('name="%s",' % item.name) item.savedIName = self.currentIName item.savedValue = self.currentValue - item.savedValuePriority = self.currentValuePriority - item.savedValueEncoding = self.currentValueEncoding item.savedType = self.currentType - item.savedTypePriority = self.currentTypePriority self.currentIName = item.iname - self.currentValuePriority = -100 - self.currentValueEncoding = None - self.currentType = "" - self.currentTypePriority = -100 + self.currentValue = ReportItem() + self.currentType = ReportItem() def exitSubItem(self, item, exType, exValue, exTraceBack): if not exType is None: @@ -327,24 +319,23 @@ class Dumper(DumperBase): self.putNumChild(0) self.putValue("") try: - typeName = self.currentType + typeName = self.currentType.value if len(typeName) > 0 and typeName != self.currentChildType: self.put('type="%s",' % typeName) # str(type.unqualified()) ? - if self.currentValue is None: + if self.currentValue.value is None: self.put('value="",numchild="0",') else: - if not self.currentValueEncoding is None: - self.put('valueencoded="%s",' % self.currentValueEncoding) - self.put('value="%s",' % self.currentValue) + if not self.currentValue.encoding is None: + self.put('valueencoded="%s",' % self.currentValue.encoding) + if self.currentValue.elided: + self.put('valueelided="%s",' % self.currentValue.elided) + self.put('value="%s",' % self.currentValue.value) except: pass self.put('},') self.currentIName = item.savedIName self.currentValue = item.savedValue - self.currentValuePriority = item.savedValuePriority - self.currentValueEncoding = item.savedValueEncoding self.currentType = item.savedType - self.currentTypePriority = item.savedTypePriority return True def isSimpleType(self, typeobj): @@ -856,14 +847,6 @@ class Dumper(DumperBase): def reportStackTop(self): self.report('stack-top={}') - def putBetterType(self, type): - try: - self.currentType = type.GetName() - except: - self.currentType = str(type) - self.currentTypePriority = self.currentTypePriority + 1 - #warn("BETTER TYPE: %s PRIORITY: %s" % (type, self.currentTypePriority)) - def extractBlob(self, base, size): if size == 0: return Blob("") diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index 0bbf9c10f4a..4718fc9614e 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -274,7 +274,7 @@ def qdump__QDateTime(d, value): tz = "" else: idBase = tzp + 2 * d.ptrSize() # [QSharedData] + [vptr] - tz = d.encodeByteArrayHelper(d.extractPointer(idBase)) + tz = d.encodeByteArrayHelper(d.extractPointer(idBase), limit=100) d.putValue("%s/%s/%s/%s/%s" % (msecs, spec, offset, tz, status), DateTimeInternal) else: @@ -705,7 +705,7 @@ def qdump__QHostAddress(d, value): ipStringAddress = privAddress + (0 if isQt5 else 24) isParsedAddress = privAddress + 24 + 2 * sizeofQString # value.d.d->ipString - ipString = d.encodeStringHelper(d.extractPointer(ipStringAddress)) + ipString = d.encodeStringHelper(d.extractPointer(ipStringAddress), limit=100) if d.extractByte(isParsedAddress) and len(ipString) > 0: d.putValue(ipString, Hex4EncodedLittleEndian) else: @@ -720,7 +720,7 @@ def qdump__QHostAddress(d, value): data = d.readMemory(privAddress + a6Offset, 16) address = ':'.join("%x" % int(data[i:i+4], 16) for i in xrange(0, 32, 4)) scopeId = privAddress + sizeofQString + (0 if isQt5 else 24) - scopeId = d.encodeStringHelper(d.extractPointer(scopeId)) + scopeId = d.encodeStringHelper(d.extractPointer(scopeId), limit=100) d.putValue("%s%%%s" % (address, scopeId), IPv6AddressAndHexScopeId) elif proto == 0: # value.d.d->a @@ -1730,7 +1730,7 @@ def qdump__QString(d, value): d.putDisplay(StopDisplay) elif format == 2: d.putField("editformat", DisplayUtf16String) - d.putField("editvalue", d.encodeString(value)) + d.putField("editvalue", d.encodeString(value, limit=None)) def qdump__QStringRef(d, value): @@ -1759,7 +1759,7 @@ def qdump__QTemporaryFile(d, value): def qdump__QTextCodec(d, value): name = d.call(value, "name") - d.putValue(d.encodeByteArray(d, name), 6) + d.putValue(d.encodeByteArray(name, limit=100), 6) d.putNumChild(2) if d.isExpanded(): with Children(d): @@ -1843,13 +1843,13 @@ def qdump__QUrl(d, value): d.putValue("") return schemeAddr = privAddress + 2 * d.intSize() - scheme = d.encodeStringHelper(d.extractPointer(schemeAddr)) - userName = d.encodeStringHelper(d.extractPointer(schemeAddr + 1 * d.ptrSize())) - password = d.encodeStringHelper(d.extractPointer(schemeAddr + 2 * d.ptrSize())) - host = d.encodeStringHelper(d.extractPointer(schemeAddr + 3 * d.ptrSize())) - path = d.encodeStringHelper(d.extractPointer(schemeAddr + 4 * d.ptrSize())) - query = d.encodeStringHelper(d.extractPointer(schemeAddr + 5 * d.ptrSize())) - fragment = d.encodeStringHelper(d.extractPointer(schemeAddr + 6 * d.ptrSize())) + scheme = d.encodeStringHelper(d.extractPointer(schemeAddr), limit=1000) + userName = d.encodeStringHelper(d.extractPointer(schemeAddr + 1 * d.ptrSize()), limit=100) + password = d.encodeStringHelper(d.extractPointer(schemeAddr + 2 * d.ptrSize()), limit=100) + host = d.encodeStringHelper(d.extractPointer(schemeAddr + 3 * d.ptrSize()), limit=100) + path = d.encodeStringHelper(d.extractPointer(schemeAddr + 4 * d.ptrSize()), limit=1000) + query = d.encodeStringHelper(d.extractPointer(schemeAddr + 5 * d.ptrSize()), limit=10000) + fragment = d.encodeStringHelper(d.extractPointer(schemeAddr + 6 * d.ptrSize()), limit=10000) port = d.extractInt(d.extractPointer(value) + d.intSize()) url = scheme diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index 149ebae29db..c929d19381b 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -418,10 +418,8 @@ def qdump__std__stringHelper1__QNX(d, value, charSize): qdump_stringHelper(d, sizePtr, size * charSize, charSize) def qdump_stringHelper(d, data, size, charSize): - cutoff = min(size, d.stringCutOff) - mem = d.readMemory(data, cutoff) - if size > d.stringCutOff: - mem += "2e2e2e" + elided, shown = d.computeLimit(size, d.displayStringLimit) + mem = d.readMemory(data, shown) if charSize == 1: encodingType = Hex2EncodedLatin1 displayType = DisplayLatin1String @@ -433,7 +431,7 @@ def qdump_stringHelper(d, data, size, charSize): displayType = DisplayUtf16String d.putNumChild(0) - d.putValue(mem, encodingType) + d.putValue(mem, encodingType, elided=elided) format = d.currentItemFormat() if format == 1: diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp index ffe72dc6aca..74fc1403af0 100644 --- a/src/plugins/debugger/commonoptionspage.cpp +++ b/src/plugins/debugger/commonoptionspage.cpp @@ -121,6 +121,14 @@ CommonOptionsPageWidget::CommonOptionsPageWidget spinBoxMaximalStringLength->setSingleStep(1000); spinBoxMaximalStringLength->setValue(10000); + labelDisplayStringLimit = new QLabel(tr("Display string limit:"), behaviorBox); + + spinBoxDisplayStringLimit = new QSpinBox(behaviorBox); + spinBoxDisplayStringLimit->setSpecialValueText(tr("")); + spinBoxDisplayStringLimit->setMaximum(10000); + spinBoxDisplayStringLimit->setSingleStep(10); + spinBoxDisplayStringLimit->setValue(100); + sourcesMappingWidget = new DebuggerSourcePathMappingWidget(this); QHBoxLayout *horizontalLayout = new QHBoxLayout(); @@ -128,6 +136,11 @@ CommonOptionsPageWidget::CommonOptionsPageWidget horizontalLayout->addWidget(spinBoxMaximalStackDepth); horizontalLayout->addStretch(); + QHBoxLayout *horizontalLayout1 = new QHBoxLayout(); + horizontalLayout1->addWidget(labelDisplayStringLimit); + horizontalLayout1->addWidget(spinBoxDisplayStringLimit); + horizontalLayout1->addStretch(); + QHBoxLayout *horizontalLayout2 = new QHBoxLayout(); horizontalLayout2->addWidget(labelMaximalStringLength); horizontalLayout2->addWidget(spinBoxMaximalStringLength); @@ -148,7 +161,8 @@ CommonOptionsPageWidget::CommonOptionsPageWidget gridLayout->addWidget(checkBoxShowQmlObjectTree, 3, 1, 1, 1); gridLayout->addWidget(checkBoxKeepEditorStationaryWhileStepping, 4, 1, 1, 1); gridLayout->addWidget(checkBoxRegisterForPostMortem, 5, 1, 1, 1); - gridLayout->addLayout(horizontalLayout2, 6, 1, 1, 2); + gridLayout->addLayout(horizontalLayout1, 6, 1, 1, 2); + gridLayout->addLayout(horizontalLayout2, 7, 1, 1, 2); QVBoxLayout *verticalLayout = new QVBoxLayout(this); verticalLayout->addWidget(behaviorBox); @@ -193,6 +207,7 @@ CommonOptionsPageWidget::CommonOptionsPageWidget m_group->insert(dc->action(UseAddressInStackView), 0); m_group->insert(dc->action(AlwaysAdjustStackColumnWidths), 0); m_group->insert(dc->action(MaximalStackDepth), spinBoxMaximalStackDepth); + m_group->insert(dc->action(DisplayStringLimit), spinBoxDisplayStringLimit); m_group->insert(dc->action(MaximalStringLength), spinBoxMaximalStringLength); m_group->insert(dc->action(ShowStdNamespace), 0); m_group->insert(dc->action(ShowQtNamespace), 0); diff --git a/src/plugins/debugger/commonoptionspage.h b/src/plugins/debugger/commonoptionspage.h index ee6fc78a1ee..b3823de55cd 100644 --- a/src/plugins/debugger/commonoptionspage.h +++ b/src/plugins/debugger/commonoptionspage.h @@ -77,9 +77,11 @@ private: QCheckBox *checkBoxWarnOnReleaseBuilds; QCheckBox *checkBoxKeepEditorStationaryWhileStepping; QLabel *labelMaximalStackDepth; + QLabel *labelDisplayStringLimit; QLabel *labelMaximalStringLength; QSpinBox *spinBoxMaximalStackDepth; QSpinBox *spinBoxMaximalStringLength; + QSpinBox *spinBoxDisplayStringLimit; DebuggerSourcePathMappingWidget *sourcesMappingWidget; const QSharedPointer m_group; diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index c36bfd0169b..4152d38d3cb 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -631,8 +631,18 @@ DebuggerSettings::DebuggerSettings() item->setDefaultValue(20); insertItem(MaximalStackDepth, item); + item = new SavedAction(this); + item->setSettingsKey(debugModeGroup, QLatin1String("DisplayStringLimit")); + item->setToolTip(tr("The maximal length of string entries in the " + "Locals and Expressions pane. Longer than that are cut off " + "and displayed with an ellipsis attached.")); + item->setDefaultValue(100); + insertItem(DisplayStringLimit, item); + item = new SavedAction(this); item->setSettingsKey(debugModeGroup, QLatin1String("MaximalStringLength")); + item->setToolTip(tr("The maximal length for strings in separated windows. " + "Longer strings are cut off and displayed with an ellipsis attached.")); item->setDefaultValue(10000); insertItem(MaximalStringLength, item); diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index b8e7488d94a..ef16a50dfe6 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -148,6 +148,7 @@ enum DebuggerActionCode AutoDerefPointers, AlwaysAdjustLocalsColumnWidths, MaximalStringLength, + DisplayStringLimit, // Source List ListSourceFiles, diff --git a/src/plugins/debugger/debuggerstreamops.cpp b/src/plugins/debugger/debuggerstreamops.cpp index 0b77e87b20b..dbd63d08612 100644 --- a/src/plugins/debugger/debuggerstreamops.cpp +++ b/src/plugins/debugger/debuggerstreamops.cpp @@ -219,7 +219,6 @@ QDataStream &operator<<(QDataStream &stream, const WatchData &wd) stream << wd.value; stream << wd.editvalue; stream << wd.editformat; - stream << wd.valuetooltip; stream << wd.typeFormats; stream << wd.type; stream << wd.displayedType; @@ -243,7 +242,6 @@ QDataStream &operator>>(QDataStream &stream, WatchData &wd) stream >> wd.value; stream >> wd.editvalue; stream >> wd.editformat; - stream >> wd.valuetooltip; stream >> wd.typeFormats; stream >> wd.type; stream >> wd.displayedType; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 12f5fa01cc5..ebcc8c5102a 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -4841,7 +4841,10 @@ void GdbEngine::updateLocalsPython(const UpdateParameters ¶ms) expanded += "formats:" + handler->individualFormatRequests(); QByteArray cutOff = " stringcutoff:" - + debuggerCore()->action(MaximalStringLength)->value().toByteArray(); + + debuggerCore()->action(MaximalStringLength)->value().toByteArray() + + " displaystringlimit:" + + debuggerCore()->action(DisplayStringLimit)->value().toByteArray(); + QByteArray watchers; const QString fileName = stackHandler()->currentFrame().file; diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp index 1f2b3cce3ec..1fa786dd771 100644 --- a/src/plugins/debugger/watchdata.cpp +++ b/src/plugins/debugger/watchdata.cpp @@ -126,6 +126,7 @@ WatchData::WatchData() : size(0), bitpos(0), bitsize(0), + elided(0), hasChildren(false), valueEnabled(true), valueEditable(true), @@ -142,12 +143,12 @@ bool WatchData::isEqual(const WatchData &other) const && name == other.name && value == other.value && editvalue == other.editvalue - && valuetooltip == other.valuetooltip && type == other.type && displayedType == other.displayedType && variable == other.variable && address == other.address && size == other.size + && elided == other.elided && hasChildren == other.hasChildren && valueEnabled == other.valueEnabled && valueEditable == other.valueEditable @@ -312,6 +313,9 @@ QString WatchData::toString() const if (isValueKnown() && !value.isEmpty()) str << "value=\"" << value << doubleQuoteComma; + if (elided) + str << "valueelided=\"" << elided << doubleQuoteComma; + if (!editvalue.isEmpty()) str << "editvalue=\"<...>\","; // str << "editvalue=\"" << editvalue << doubleQuoteComma; @@ -382,7 +386,7 @@ QString WatchData::toToolTip() const formatToolTipRow(str, tr("Internal Type"), QLatin1String(type)); if (!displayedType.isEmpty()) formatToolTipRow(str, tr("Displayed Type"), displayedType); - QString val = valuetooltip.isEmpty() ? value : valuetooltip; + QString val = value; // Automatically display hex value for unsigned integers. if (!val.isEmpty() && val.at(0).isDigit() && isIntType(type)) { bool ok; @@ -458,13 +462,6 @@ void WatchData::updateValue(const GdbMi &item) } } -void setWatchDataValueToolTip(WatchData &data, const GdbMi &mi, - int encoding) -{ - if (mi.isValid()) - data.valuetooltip = decodeData(mi.data(), encoding); -} - void WatchData::updateChildCount(const GdbMi &mi) { if (mi.isValid()) @@ -629,6 +626,10 @@ void parseWatchData(const QSet &expandedINames, if (mi.isValid()) data.typeFormats = QString::fromUtf8(mi.data()); + mi = item["valueelided"]; + if (mi.isValid()) + data.elided = mi.toInt(); + mi = item["bitpos"]; if (mi.isValid()) data.bitpos = mi.toInt(); diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h index 94e92ce1444..4d179d72f4e 100644 --- a/src/plugins/debugger/watchdata.h +++ b/src/plugins/debugger/watchdata.h @@ -126,7 +126,6 @@ public: QString value; // Displayed value QByteArray editvalue; // Displayed value qint32 editformat; // Format of displayed value - QString valuetooltip; // Tooltip in value column QString typeFormats; // Selection of formats of displayed value QByteArray type; // Type for further processing QString displayedType;// Displayed type (optional) @@ -135,6 +134,7 @@ public: uint size; // Size uint bitpos; // Position within bit fields uint bitsize; // Size in case of bit fields + int elided; // Full size if value was cut off, -1 if cut on unknown size, 0 otherwise bool hasChildren; bool valueEnabled; // Value will be enabled or not bool valueEditable; // Value will be editable diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 55a06309761..652ae985587 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -695,6 +695,14 @@ QString WatchModel::formattedValue(const WatchData &data) const } } + if (data.elided) { + QString v = value; + v.chop(1); + QString len = data.elided > 0 ? QString::number(data.elided) + : QLatin1String("unknown length"); + return v + QLatin1String("\"... (") + len + QLatin1Char(')'); + } + return translate(value); }