aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhjk <hjk@qt.io>2024-04-26 13:48:06 +0200
committerhjk <hjk@qt.io>2024-05-16 10:03:08 +0000
commit6aab6f61b59e3d052115f4d68d5dc9ed24cc9843 (patch)
tree167f3b34740e0905d8aa43a2547b7cea87412c29
parentbee7cdfd1ecfdc6bf27310e500647d321929f5b6 (diff)
Debugger: Provider Qt version externally to bridges
Extracting within the bridges is expensive. Change-Id: Icf69db4b112230cc23e331abc0b3eb0de1323f46 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: David Schulz <david.schulz@qt.io>
-rw-r--r--share/qtcreator/debugger/dumper.py27
-rw-r--r--share/qtcreator/debugger/gdbbridge.py98
-rw-r--r--share/qtcreator/debugger/lldbbridge.py78
-rw-r--r--src/plugins/debugger/cdb/cdbengine.cpp6
-rw-r--r--src/plugins/debugger/debuggerengine.h3
-rw-r--r--src/plugins/debugger/debuggerruncontrol.cpp2
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp6
-rw-r--r--src/plugins/debugger/lldb/lldbengine.cpp6
-rw-r--r--tests/auto/debugger/tst_dumpers.cpp45
9 files changed, 89 insertions, 182 deletions
diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py
index 488f1e362e..ec7b83d331 100644
--- a/share/qtcreator/debugger/dumper.py
+++ b/share/qtcreator/debugger/dumper.py
@@ -172,9 +172,11 @@ class DumperBase():
self.qtCustomEventFunc = 0
self.qtCustomEventPltFunc = 0
self.qtPropertyFunc = 0
- self.fallbackQtVersion = 0x60200
+ self.qtversion = None
+ self.qtns = None
self.passExceptions = False
self.isTesting = False
+ self.qtLoaded = False
self.isBigEndian = False
self.packCode = '<'
@@ -231,6 +233,8 @@ class DumperBase():
self.useTimeStamps = int(args.get('timestamps', '0'))
self.partialVariable = args.get('partialvar', '')
self.uninitialized = args.get('uninitialized', [])
+ self.qtversion = args.get('qtversion', 0x060602)
+ self.qtnamespace = args.get('qtnamespace', '')
self.uninitialized = list(map(lambda x: self.hexdecode(x), self.uninitialized))
#self.warn('NAMESPACE: "%s"' % self.qtNamespace())
#self.warn('EXPANDED INAMES: %s' % self.expandedINames)
@@ -252,9 +256,11 @@ class DumperBase():
args['partialvar'] = ''
self.fetchVariables(args)
- def setFallbackQtVersion(self, args):
- version = int(args.get('version', self.fallbackQtVersion))
- self.fallbackQtVersion = version
+ def qtVersion(self):
+ return self.qtversion
+
+ def qtNamespace(self):
+ return self.qtnamespace
def resetPerStepCaches(self):
self.perStepCache = {}
@@ -1566,7 +1572,16 @@ class DumperBase():
if address is not None:
self.put('origaddr="0x%x",' % address)
+ def wantQObjectNames(self):
+ return self.showQObjectNames and self.qtLoaded
+
+ def fetchInternalFunctions(self):
+ # Overrridden
+ pass
+
def putQObjectNameValue(self, value):
+ self.fetchInternalFunctions()
+
try:
# dd = value['d_ptr']['d'] is just behind the vtable.
(vtable, dd) = self.split('pp', value)
@@ -3022,7 +3037,7 @@ typename))
self.putExpandable()
self.putEmptyValue()
#self.warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address()))
- if self.showQObjectNames:
+ if self.wantQObjectNames():
#with self.timer(self.currentIName):
self.putQObjectNameValue(value)
if self.isExpanded():
@@ -3030,7 +3045,7 @@ typename))
self.putField('sortable', 1)
with Children(self, 1, childType=None):
self.putFields(value)
- if self.showQObjectNames:
+ if self.wantQObjectNames():
self.tryPutQObjectGuts(value)
def symbolAddress(self, symbolName):
diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py
index 744d246142..941c9b8312 100644
--- a/share/qtcreator/debugger/gdbbridge.py
+++ b/share/qtcreator/debugger/gdbbridge.py
@@ -852,37 +852,6 @@ class Dumper(DumperBase):
except:
return '0x%x' % address
- def qtVersionString(self):
- try:
- return str(gdb.lookup_symbol('qVersion')[0].value()())
- except:
- pass
- try:
- ns = self.qtNamespace()
- return str(gdb.parse_and_eval("((const char*(*)())'%sqVersion')()" % ns))
- except:
- pass
- return None
-
- def qtVersion(self):
- try:
- # Only available with Qt 5.3+
- qtversion = int(str(gdb.parse_and_eval('((void**)&qtHookData)[2]')), 16)
- self.qtVersion = lambda: qtversion
- return qtversion
- except:
- pass
-
- try:
- version = self.qtVersionString()
- (major, minor, patch) = version[version.find('"') + 1:version.rfind('"')].split('.')
- qtversion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
- self.qtVersion = lambda: qtversion
- return qtversion
- except:
- # Use fallback until we have a better answer.
- return self.fallbackQtVersion
-
def createSpecialBreakpoints(self, args):
self.specialBreakpoints = []
@@ -1008,10 +977,6 @@ class Dumper(DumperBase):
for obj in gdb.objfiles():
self.importPlainDumpersForObj(obj)
- def qtNamespace(self):
- # This function is replaced by handleQtCoreLoaded()
- return ''
-
def findSymbol(self, symbolName):
try:
return int(gdb.parse_and_eval("(size_t)&'%s'" % symbolName))
@@ -1044,41 +1009,38 @@ class Dumper(DumperBase):
pass
def handleQtCoreLoaded(self, objfile):
- fd, tmppath = tempfile.mkstemp()
- os.close(fd)
- cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath)
- symbols = gdb.execute(cmd, to_string=True)
- ns = ''
- with open(tmppath) as f:
- ns1re = re.compile(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ')
- ns2re = re.compile(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ')
- for line in f:
- if 'msgHandlerGrabbed ' in line:
- # [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
- # section .tbss Myns::msgHandlerGrabbed qlogging.cpp
- ns = ns1re.split(line)[2]
- if len(ns):
- ns += '::'
- break
- if 'currentThreadData ' in line:
- # [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE
- # section .tbss UU::currentThreadData qthread_unix.cpp\\n
- ns = ns2re.split(line)[2]
- if len(ns):
- ns += '::'
- break
- os.remove(tmppath)
-
+ self.qtLoaded = True
+ # FIXME: Namespace auto-detection. Is it worth the price?
+ # fd, tmppath = tempfile.mkstemp()
+ # os.close(fd)
+ # cmd = 'maint print msymbols -objfile "%s" -- %s' % (objfile.filename, tmppath)
+ # symbols = gdb.execute(cmd, to_string=True)
+ # ns = ''
+ # with open(tmppath) as f:
+ # ns1re = re.compile(r'_ZN?(\d*)(\w*)L17msgHandlerGrabbedE? ')
+ # ns2re = re.compile(r'_ZN?(\d*)(\w*)L17currentThreadDataE? ')
+ # for line in f:
+ # if 'msgHandlerGrabbed ' in line:
+ # # [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
+ # # section .tbss Myns::msgHandlerGrabbed qlogging.cpp
+ # ns = ns1re.split(line)[2]
+ # if len(ns):
+ # ns += '::'
+ # break
+ # if 'currentThreadData ' in line:
+ # # [ 0] b 0x7ffff67d3000 _ZN2UUL17currentThreadDataE
+ # # section .tbss UU::currentThreadData qthread_unix.cpp\\n
+ # ns = ns2re.split(line)[2]
+ # if len(ns):
+ # ns += '::'
+ # break
+ # os.remove(tmppath)
+
+ def fetchInternalFunctions(self):
+ ns = self.qtNamespace()
lenns = len(ns)
strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
-
if lenns:
- # 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.
- self.qtNamespaceToReport = ns
- self.qtNamespace = lambda: ns
-
sym = '_ZN%s7QObject11customEventEPNS_6QEventE' % strns
else:
sym = '_ZN7QObject11customEventEP6QEvent'
@@ -1091,6 +1053,8 @@ class Dumper(DumperBase):
if not self.isWindowsTarget(): # prevent calling the property function on windows
self.qtPropertyFunc = self.findSymbol(sym)
+ self.fetchInternalFunctions = lambda: None
+
def assignValue(self, args):
type_name = self.hexdecode(args['type'])
expr = self.hexdecode(args['expr'])
diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py
index 6b08ac3d32..d8c0ab6ca6 100644
--- a/share/qtcreator/debugger/lldbbridge.py
+++ b/share/qtcreator/debugger/lldbbridge.py
@@ -768,80 +768,7 @@ class Dumper(DumperBase):
symbol = funcs[0].GetSymbol()
self.qtPropertyFunc = symbol.GetStartAddress().GetLoadAddress(self.target)
- def fetchQtVersionAndNamespace(self):
- for func in self.target.FindFunctions('qVersion'):
- name = func.GetSymbol().GetName()
- if name == None:
- continue
- if name.endswith('()'):
- name = name[:-2]
- if name.count(':') > 2:
- continue
-
- qtNamespace = name[:name.find('qVersion')]
- self.qtNamespace = lambda: qtNamespace
-
- options = lldb.SBExpressionOptions()
- res = self.target.EvaluateExpression(name + '()', options)
-
- if not res.IsValid() or not res.GetType().IsPointerType():
- exp = '((const char*())%s)()' % name
- res = self.target.EvaluateExpression(exp, options)
-
- if not res.IsValid() or not res.GetType().IsPointerType():
- exp = '((const char*())_Z8qVersionv)()'
- res = self.target.EvaluateExpression(exp, options)
-
- if not res.IsValid() or not res.GetType().IsPointerType():
- continue
-
- version = str(res)
- if version.count('.') != 2:
- continue
-
- version.replace("'", '"') # Both seem possible
- version = version[version.find('"') + 1:version.rfind('"')]
-
- (major, minor, patch) = version.split('.')
- qtVersion = 0x10000 * int(major) + 0x100 * int(minor) + int(patch)
- self.qtVersion = lambda: qtVersion
-
- return (qtNamespace, qtVersion)
-
- try:
- versionValue = self.target.EvaluateExpression('qtHookData[2]').GetNonSyntheticValue()
- if versionValue.IsValid():
- return ('', versionValue.unsigned)
- except:
- pass
-
- return ('', self.fallbackQtVersion)
-
- def qtVersionAndNamespace(self):
- qtVersionAndNamespace = None
- try:
- qtVersionAndNamespace = self.fetchQtVersionAndNamespace()
- self.report("Detected Qt Version: 0x%0x (namespace='%s')" %
- (qtVersionAndNamespace[1], qtVersionAndNamespace[0] or "no namespace"))
- except Exception as e:
- DumperBase.warn('[lldb] Error detecting Qt version: %s' % e)
-
- try:
- self.fetchInternalFunctions()
- self.report('Found function QObject::property: 0x%0x' % self.qtPropertyFunc)
- self.report('Found function QObject::customEvent: 0x%0x' % self.qtCustomEventFunc)
- except Exception as e:
- DumperBase.warn('[lldb] Error fetching internal Qt functions: %s' % e)
-
- # Cache version information by overriding this function.
- self.qtVersionAndNamespace = lambda: qtVersionAndNamespace
- return qtVersionAndNamespace
-
- def qtNamespace(self):
- return self.qtVersionAndNamespace()[0]
-
- def qtVersion(self):
- return self.qtVersionAndNamespace()[1]
+ self.fetchInternalFunctions = lambda: None
def handleCommand(self, command):
result = lldb.SBCommandReturnObject()
@@ -1360,6 +1287,9 @@ class Dumper(DumperBase):
self.setVariableFetchingOptions(args)
+ self.qtLoaded = True # FIXME: Do that elsewhere
+
+
# Reset certain caches whenever a step over / into / continue
# happens.
# FIXME: Caches are currently also cleared if currently
diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index 83c5a9459b..24386788db 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -1042,6 +1042,8 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters)
cmd.arg("partialvar", updateParameters.partialVariable);
cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("timestamps", s.logTimeStamps());
+ cmd.arg("qtversion", runParameters().qtVersion);
+ cmd.arg("qtnamespace", runParameters().qtNamespace);
StackFrame frame = stackHandler()->currentFrame();
cmd.arg("context", frame.context);
@@ -2809,10 +2811,6 @@ void CdbEngine::setupScripting(const DebuggerResponse &response)
runCommand({command, ScriptCommand});
}
- DebuggerCommand cmd0("theDumper.setFallbackQtVersion", ScriptCommand);
- cmd0.arg("version", runParameters().fallbackQtVersion);
- runCommand(cmd0);
-
runCommand({"theDumper.loadDumpers(None)", ScriptCommand,
[this](const DebuggerResponse &response) {
watchHandler()->addDumpers(response.data["result"]["dumpers"]);
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index e82a386311..b5e8a3cba8 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -188,7 +188,8 @@ public:
QStringList validationErrors;
- int fallbackQtVersion = 0x50200;
+ int qtVersion = 0;
+ QString qtNamespace;
// Common debugger constants.
Utils::FilePath peripheralDescriptionFile;
diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp
index 503bad4bc2..522191e25b 100644
--- a/src/plugins/debugger/debuggerruncontrol.cpp
+++ b/src/plugins/debugger/debuggerruncontrol.cpp
@@ -898,7 +898,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm
if (QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(kit)) {
const QVersionNumber qtVersion = baseQtVersion->qtVersion();
- m_runParameters.fallbackQtVersion = 0x10000 * qtVersion.majorVersion()
+ m_runParameters.qtVersion = 0x10000 * qtVersion.majorVersion()
+ 0x100 * qtVersion.minorVersion()
+ qtVersion.microVersion();
}
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index e4873de775..3abaea75a4 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -4054,10 +4054,6 @@ void GdbEngine::handleGdbStarted()
if (!commands.isEmpty())
runCommand({commands});
- DebuggerCommand cmd1("setFallbackQtVersion");
- cmd1.arg("version", rp.fallbackQtVersion);
- runCommand(cmd1);
-
runCommand({"loadDumpers", CB(handlePythonSetup)});
// Reload peripheral register description.
@@ -5133,6 +5129,8 @@ void GdbEngine::doUpdateLocals(const UpdateParameters &params)
cmd.arg("dyntype", s.useDynamicType());
cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("timestamps", s.logTimeStamps());
+ cmd.arg("qtversion", runParameters().qtVersion);
+ cmd.arg("qtnamespace", runParameters().qtNamespace);
StackFrame frame = stackHandler()->currentFrame();
cmd.arg("context", frame.context);
diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp
index 3d21f82ca2..a2b8c4eb3d 100644
--- a/src/plugins/debugger/lldb/lldbengine.cpp
+++ b/src/plugins/debugger/lldb/lldbengine.cpp
@@ -346,10 +346,6 @@ void LldbEngine::handleLldbStarted()
cmd2.flags = Silent;
runCommand(cmd2);
-
- DebuggerCommand cmd0("setFallbackQtVersion");
- cmd0.arg("version", rp.fallbackQtVersion);
- runCommand(cmd0);
}
void LldbEngine::runEngine()
@@ -767,6 +763,8 @@ void LldbEngine::doUpdateLocals(const UpdateParameters &params)
cmd.arg("partialvar", params.partialVariable);
cmd.arg("qobjectnames", s.showQObjectNames());
cmd.arg("timestamps", s.logTimeStamps());
+ cmd.arg("qtversion", runParameters().qtVersion);
+ cmd.arg("qtnamespace", runParameters().qtNamespace);
StackFrame frame = stackHandler()->currentFrame();
cmd.arg("context", frame.context);
diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp
index e5763e847e..4385f84084 100644
--- a/tests/auto/debugger/tst_dumpers.cpp
+++ b/tests/auto/debugger/tst_dumpers.cpp
@@ -1200,7 +1200,28 @@ void tst_Dumpers::initTestCase()
if (qEnvironmentVariableIntValue("QTC_USE_CMAKE_FOR_TEST"))
m_buildSystem = BuildSystem::CMake;
+ QProcess qmake;
+ qmake.start(m_qmakeBinary, {"--version"});
+ QVERIFY(qmake.waitForFinished());
+ QByteArray output = qmake.readAllStandardOutput();
+ QByteArray error = qmake.readAllStandardError();
+ int pos0 = output.indexOf("Qt version");
+ if (pos0 == -1) {
+ qCDebug(lcDumpers).noquote() << "Output: " << output;
+ qCDebug(lcDumpers).noquote() << "Error: " << error;
+ QVERIFY(false);
+ }
+ pos0 += 11;
+ int pos1 = output.indexOf('.', pos0 + 1);
+ int major = output.mid(pos0, pos1++ - pos0).toInt();
+ int pos2 = output.indexOf('.', pos1 + 1);
+ int minor = output.mid(pos1, pos2++ - pos1).toInt();
+ int pos3 = output.indexOf(' ', pos2 + 1);
+ int patch = output.mid(pos2, pos3++ - pos2).toInt();
+ m_qtVersion = 0x10000 * major + 0x100 * minor + patch;
+
qCDebug(lcDumpers) << "QMake : " << m_qmakeBinary;
+ qCDebug(lcDumpers) << "Qt Version : " << m_qtVersion;
qCDebug(lcDumpers) << "Use CMake : " << (m_buildSystem == BuildSystem::CMake) << int(m_buildSystem);
m_useGLibCxxDebug = qgetenv("QTC_USE_GLIBCXXDEBUG_FOR_TEST").toInt();
@@ -1381,27 +1402,6 @@ void tst_Dumpers::dumper()
QByteArray error;
if (data.neededQtVersion.isRestricted) {
- QProcess qmake;
- qmake.setWorkingDirectory(t->buildPath);
- qmake.start(m_qmakeBinary, {"--version"});
- QVERIFY(qmake.waitForFinished());
- output = qmake.readAllStandardOutput();
- error = qmake.readAllStandardError();
- int pos0 = output.indexOf("Qt version");
- if (pos0 == -1) {
- qCDebug(lcDumpers).noquote() << "Output: " << output;
- qCDebug(lcDumpers).noquote() << "Error: " << error;
- QVERIFY(false);
- }
- pos0 += 11;
- int pos1 = output.indexOf('.', pos0 + 1);
- int major = output.mid(pos0, pos1++ - pos0).toInt();
- int pos2 = output.indexOf('.', pos1 + 1);
- int minor = output.mid(pos1, pos2++ - pos1).toInt();
- int pos3 = output.indexOf(' ', pos2 + 1);
- int patch = output.mid(pos2, pos3++ - pos2).toInt();
- m_qtVersion = 0x10000 * major + 0x100 * minor + patch;
-
if (data.neededQtVersion.min > m_qtVersion)
MSKIP_SINGLE(QByteArray("Need minimum Qt version "
+ QByteArray::number(data.neededQtVersion.min, 16)));
@@ -1793,6 +1793,7 @@ void tst_Dumpers::dumper()
"python theDumper.fetchVariables({" + dumperOptions +
"'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1,"
+ "'qtversion':" + QString::number(m_qtVersion) + ",'qtnamespace':'',"
"'testing':1,'qobjectnames':1,"
"'expanded':{" + expandedq + "}})\n";
@@ -1816,6 +1817,7 @@ void tst_Dumpers::dumper()
"'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':0,"
"'testing':1,'qobjectnames':1,"
+ "'qtversion':" + QString::number(m_qtVersion) + ",'qtnamespace':'',"
"'expanded':{" + expandedq + "}})\n"
"q\n";
} else if (m_debuggerEngine == LldbEngine) {
@@ -1840,6 +1842,7 @@ void tst_Dumpers::dumper()
"'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1,"
"'testing':1,'qobjectnames':1,"
+ "'qtversion':" + QString::number(m_qtVersion) + ",'qtnamespace':'',"
"'expanded':{" + expandedq + "}})\n"
"quit\n";