diff options
author | hjk <hjk@qt.io> | 2016-12-01 09:22:53 +0100 |
---|---|---|
committer | Christian Stenger <christian.stenger@qt.io> | 2016-12-02 11:23:46 +0000 |
commit | 2275d683739ac90602d28bde5b8455d075e9cc37 (patch) | |
tree | e608e18448fdb57cf328c6250edf07f10492ea03 | |
parent | a9b5e518d0408aad0b140c6465dd080a072d8d66 (diff) | |
download | qt-creator-2275d683739ac90602d28bde5b8455d075e9cc37.tar.gz |
Debugger: Switch on QObject name display by default
... and use the option to govern general QObject guts display.
This allows people to completely avoid the performance impact
of attempted QObject display and still makes the feature
more prominent for our favorite use case.
Change-Id: I1e53b6448f646ab7eea9168a3cd24c77769e6328
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
-rw-r--r-- | share/qtcreator/debugger/dumper.py | 120 | ||||
-rw-r--r-- | share/qtcreator/debugger/gdbbridge.py | 11 | ||||
-rw-r--r-- | share/qtcreator/debugger/lldbbridge.py | 5 | ||||
-rw-r--r-- | src/plugins/debugger/debuggeractions.cpp | 6 | ||||
-rw-r--r-- | tests/auto/debugger/tst_dumpers.cpp | 8 |
5 files changed, 62 insertions, 88 deletions
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 1bb9df7437..3f820ddf9a 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -253,6 +253,7 @@ class DumperBase: self.typesReported = {} self.typesToReport = {} self.qtNamespaceToReport = None + self.qtCustomEventFunc = 0 self.passExceptions = False self.isTesting = False @@ -303,7 +304,7 @@ class DumperBase: self.forceQtNamespace = int(args.get('forcens', '0')) self.passExceptions = int(args.get('passexceptions', '0')) self.isTesting = int(args.get('testing', '0')) - self.showQObjectNames = int(args.get('qobjectnames', '0')) + self.showQObjectNames = int(args.get('qobjectnames', '1')) self.nativeMixed = int(args.get('nativemixed', '0')) self.autoDerefPointers = int(args.get('autoderef', '0')) self.partialVariable = args.get('partialvar', '') @@ -1373,11 +1374,13 @@ class DumperBase: def putQObjectNameValue(self, value): try: - intSize = 4 - ptrSize = self.ptrSize() # dd = value['d_ptr']['d'] is just behind the vtable. (vtable, dd) = self.split('pp', value) + if not self.couldBeQObjectVTable(vtable): + return False + intSize = 4 + ptrSize = self.ptrSize() if self.qtVersion() < 0x050000: # Size of QObjectData: 5 pointer + 2 int # - vtable @@ -1428,97 +1431,47 @@ class DumperBase: except: # warn('NO QOBJECT: %s' % value.type) - pass + return False - def canBePointer(self, p): + def couldBePointer(self, p): if self.ptrSize() == 4: return p > 100000 and (p & 0x3 == 0) else: return p > 100000 and (p & 0x7 == 0) and (p < 0x7fffffffffff) - def canBeVTableEntry(self, p): + def couldBeVTableEntry(self, p): if self.ptrSize() == 4: return p > 100000 and (p & 0x1 == 0) else: return p > 100000 and (p & 0x1 == 0) and (p < 0x7fffffffffff) - def couldBeQObject(self, objectPtr): + def couldBeQObjectPointer(self, objectPtr): try: - (vtablePtr, dd) = self.split('pp', objectPtr) + vtablePtr, dd = self.split('pp', objectPtr) except: self.bump('nostruct-1') return False - if not self.canBePointer(vtablePtr): - self.bump('vtable') - return False - if not self.canBePointer(dd): - self.bump('d_d_ptr') - return False - - try: - metaObjectFunc, metaCastFunc, metaCallFunc = self.split('ppp', vtablePtr) - except: - return False - - # The first three entries are in a fairly rigid relationship defined - # by the Q_OBJECT macro. - if not self.canBeVTableEntry(metaObjectFunc): - return False - if not self.canBeVTableEntry(metaCastFunc): - return False - if not self.canBeVTableEntry(metaCallFunc): - return False - if metaCastFunc < metaObjectFunc or metaCastFunc > metaObjectFunc + 200: - # The metaObject implementation is just that: - # QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() - # : &staticMetaObject; - # That should not exceed 200 bytes. Observed on x86_64 debug 72. - return False - if metaCallFunc < metaCastFunc or metaCallFunc > metaCastFunc + 200: - # if (!_clname) return nullptr; - # if (!strcmp(_clname, qt_meta_stringdata_Bar__TestObject.stringdata0)) - # return static_cast<void*>(const_cast< TestObject*>(this)); - # return QWidget::qt_metacast(_clname); - # That should not exceed 200 bytes. Observed on x86_64 debug 80. - return False - try: - (dvtablePtr, qptr, parentPtr, childrenDPtr, flags) \ - = self.split('ppppI', dd) + dvtablePtr, qptr, parentPtr = self.split('ppp', dd) except: self.bump('nostruct-2') return False - #warn('STRUCT DD: %s 0x%x' % (self.currentIName, qptr)) - if not self.canBePointer(dvtablePtr): - self.bump('dvtable') - #warn('DVT: 0x%x' % dvtablePtr) - return False # Check d_ptr.d.q_ptr == objectPtr if qptr != objectPtr: - #warn('QPTR: 0x%x 0x%x' % (qptr, objectPtr)) self.bump('q_ptr') return False - if parentPtr and not self.canBePointer(parentPtr): - #warn('PAREN') - self.bump('parent') - return False - if not self.canBePointer(childrenDPtr): - #warn('CHILD') - self.bump('children') + + return self.couldBeQObjectVTable(vtablePtr) + + def couldBeQObjectVTable(self, vtablePtr): + try: + customEventFunc = self.extractPointer(vtablePtr + 9 * self.ptrSize()) + except: + self.bump('nostruct-3') return False - #if flags >= 0x80: # Only 7 flags are defined - # warn('FLAGS: 0x%x %s' % (flags, self.currentIName)) - # self.bump('flags') - # return False - #warn('OK') - #if dynMetaObjectPtr and not self.canBePointer(dynMetaObjectPtr): - # self.bump('dynmo') - # return False - - self.bump('couldBeQObject') - return True + return self.qtCustomEventFunc == customEventFunc def extractMetaObjectPtr(self, objectPtr, typeobj): """ objectPtr - address of *potential* instance of QObject derived class @@ -1528,7 +1481,7 @@ class DumperBase: self.checkIntType(objectPtr) def extractMetaObjectPtrFromAddress(): - return 0 + #return 0 # FIXME: Calling 'works' but seems to impact memory contents(!) # in relevant places. One symptom is that object name # contents 'vanishes' as the reported size of the string @@ -1542,7 +1495,7 @@ class DumperBase: res = self.parseAndEvaluate(cmd) #warn('MO RES: %s' % res) self.bump('successfulMetaObjectCall') - return toInteger(res) + return res.pointer() except: self.bump('failedMetaObjectCall') #warn('COULD NOT EXECUTE: %s' % cmd) @@ -1594,7 +1547,8 @@ class DumperBase: # if base is not None and base != someTypeObj: # sanity check # result = extractStaticMetaObjectPtrFromType(base) - self.knownStaticMetaObjects[someTypeName] = result + if result: + self.knownStaticMetaObjects[someTypeName] = result return result @@ -1610,7 +1564,7 @@ class DumperBase: #warn('CACHED RESULT: %s %s 0x%x' % (self.currentIName, typeName, result)) return result - if not self.couldBeQObject(objectPtr): + if not self.couldBeQObjectPointer(objectPtr): self.bump('cannotBeQObject') #warn('DOES NOT LOOK LIKE A QOBJECT: %s' % self.currentIName) return 0 @@ -1690,8 +1644,11 @@ class DumperBase: self.putNumChild(0) # This is called is when a QObject derived class is expanded - def putQObjectGuts(self, qobject, metaObjectPtr): - self.putQObjectGutsHelper(qobject, qobject.address(), -1, metaObjectPtr, 'QObject') + def tryPutQObjectGuts(self, value): + metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) + if metaObjectPtr: + self.putQObjectGutsHelper(value, value.address(), + -1, metaObjectPtr, 'QObject') def metaString(self, metaObjectPtr, index, revision): ptrSize = self.ptrSize() @@ -2686,23 +2643,16 @@ class DumperBase: self.putNumChild(1) self.putEmptyValue() #warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address())) - metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) if self.showQObjectNames: self.preping(self.currentIName) - metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) - self.ping(self.currentIName) - if metaObjectPtr: - self.context = value self.putQObjectNameValue(value) - #warn('STRUCT GUTS: %s MO: 0x%x ' % (self.currentIName, metaObjectPtr)) + self.ping(self.currentIName) if self.isExpanded(): self.putField('sortable', 1) with Children(self, 1, childType=None): self.putFields(value) - if not self.showQObjectNames: - metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) - if metaObjectPtr: - self.putQObjectGuts(value, metaObjectPtr) + if self.showQObjectNames: + self.tryPutQObjectGuts(value) def symbolAddress(self, symbolName): res = self.parseAndEvaluate('(size_t)&' + symbolName) @@ -3282,7 +3232,7 @@ class DumperBase: except: return None #warn('VTBL: 0x%x' % vtbl) - if not self.dumper.canBePointer(vtbl): + if not self.dumper.couldBePointer(vtbl): return None return self.dumper.nativeDynamicTypeName(address, self) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index f78b5fc6f4..a84293d35c 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -1039,6 +1039,17 @@ class Dumper(DumperBase): except: pass + try: + lenns = len(ns) + if lenns: + sym = '_ZN%d%s7QObject11customEventEPNS_6QEventE' % \ + (lenns - 2, ns[:lenns - 2]) + else: + sym = '_ZN7QObject11customEventEP6QEvent' + self.qtCustomEventFunc = toInteger(gdb.parse_and_eval('(size_t)&%s' % sym)) + except: + pass + # This might be wrong, but we can't do better: We found # a libQt5Core and could not extract a namespace. # The best guess is that there isn't any. diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index b54a34b4f4..9ac37beeb6 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -679,6 +679,11 @@ class Dumper(DumperBase): qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch) self.qtVersion = lambda: qtVersion + funcs = self.target.FindFunctions('QObject::customEvent') + if len(funcs): + symbol = funcs[0].GetSymbol() + self.qtCustomEventFunc = symbol.GetStartAddress().GetLoadAddress(self.target) + return (qtNamespace, qtVersion) return ('', 0x50200) diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index 5c225aed95..4e4b28bd61 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -257,15 +257,15 @@ DebuggerSettings::DebuggerSettings() insertItem(ShowQtNamespace, item); item = new SavedAction(this); - item->setSettingsKey(debugModeGroup, QLatin1String("ShowQObjectNames")); + item->setSettingsKey(debugModeGroup, QLatin1String("ShowQObjectNames2")); item->setText(tr("Show QObject names if available")); item->setDialogText(tr("Show QObject names if available")); item->setToolTip(tr("<p>Displays the objectName property of QObject based items. " "Note that this can negatively impact debugger performance " "even if no QObjects are present.")); item->setCheckable(true); - item->setDefaultValue(false); - item->setValue(false); + item->setDefaultValue(true); + item->setValue(true); insertItem(ShowQObjectNames, item); item = new SavedAction(this); diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 0d1126a17f..48e10ce52d 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -3398,6 +3398,14 @@ void tst_Dumpers::dumper_data() "{\n" " void run()\n" " {\n" + " auto mo = &QThread::metaObject;\n" + " auto mc = &QThread::qt_metacast;\n" + " auto p0 = (*(void***)this)[0]; unused(&p0);\n" + " auto p1 = (*(void***)this)[1]; unused(&p1);\n" + " auto p2 = (*(void***)this)[2]; unused(&p2);\n" + " auto p3 = (*(void***)this)[3]; unused(&p3);\n" + " auto p4 = (*(void***)this)[4]; unused(&p4);\n" + " auto p5 = (*(void***)this)[5]; unused(&p5);\n" " if (m_id == 3) {\n" " BREAK;\n" " }\n" |