static ProbeABI abiFromMachO(const uchar* data, qint64 size) { ProbeABI abi; const quint32 magic = *reinterpret_cast<const quint32*>(data); quint32 offset = 0; qint32 ncmds = 0; qint32 cmdsize = 0; switch (magic) { case MH_MAGIC: abi.setArchitecture(readMachOHeader<mach_header>(data, size, offset, ncmds, cmdsize)); break; case MH_MAGIC_64: abi.setArchitecture(readMachOHeader<mach_header_64>(data, size, offset, ncmds, cmdsize)); break; } if (offset >= size || ncmds <= 0 || cmdsize <= 0 || size <= offset + cmdsize) return ProbeABI(); // read load commands for (int i = 0; i < ncmds; ++i) { const load_command* cmd = reinterpret_cast<const load_command*>(data + offset); if (cmd->cmd == LC_ID_DYLIB) { const dylib_command* dlcmd = reinterpret_cast<const dylib_command*>(data + offset); const int majorVersion = (dlcmd->dylib.current_version & 0x00ff0000) >> 16; const int minorVersion = (dlcmd->dylib.current_version & 0x0000ff00) >> 8; abi.setQtVersion(majorVersion, minorVersion); } offset += cmd->cmdsize; } return abi; }
void testDetectProcess() { ProbeABIDetector detector; QVERIFY(!detector.qtCoreForProcess(QCoreApplication::applicationPid()).isEmpty()); const ProbeABI abi = detector.abiForProcess(QCoreApplication::applicationPid()); QCOMPARE(abi.id(), QStringLiteral(GAMMARAY_PROBE_ABI)); }
void testDisplayString() { QFETCH(QString, id); QFETCH(QString, display); const ProbeABI abi = ProbeABI::fromString(id); QCOMPARE(abi.displayString(), display); }
bool ProbeABI::operator==(const ProbeABI& rhs) const { return majorQtVersion() == rhs.majorQtVersion() && minorQtVersion() == rhs.minorQtVersion() && architecture() == rhs.architecture() && compiler() == rhs.compiler() && isDebug() == rhs.isDebug(); }
void testFromString() { QFETCH(QString, id); QFETCH(bool, valid); QFETCH(int, majorVersion); QFETCH(int, minorVersion); QFETCH(bool, isDebug); QFETCH(QString, arch); QFETCH(QString, compiler); #ifdef Q_OS_WIN QFETCH(QString, compilerVersion); #endif const ProbeABI abi = ProbeABI::fromString(id); QCOMPARE(abi.isValid(), valid); if (!valid) return; QCOMPARE(abi.majorQtVersion(), majorVersion); QCOMPARE(abi.minorQtVersion(), minorVersion); QCOMPARE(abi.architecture(), arch); if (abi.isDebugRelevant()) { QCOMPARE(abi.isDebug(), isDebug); } #ifdef Q_OS_WIN QCOMPARE(abi.compiler(), compiler); QCOMPARE(abi.compilerVersion(), compilerVersion); #else Q_UNUSED(compiler); #endif }
static ProbeABI qtVersionFromFileName(const QString &path) { ProbeABI abi; const QStringList parts = path.split('.'); if (parts.size() < 4 || parts.at(parts.size() - 4) != QLatin1String("so")) return abi; abi.setQtVersion(parts.at(parts.size() - 3).toInt(), parts.at(parts.size() - 2).toInt()); return abi; }
bool ProbeABI::isCompatible(const ProbeABI& referenceABI) const { return d->majorQtVersion == referenceABI.majorQtVersion() && d->minorQtVersion >= referenceABI.minorQtVersion() // we can work with older probes, since the target defines the Qt libraries being used && d->architecture == referenceABI.architecture() #ifdef Q_OS_WIN && d->compiler == referenceABI.compiler() #endif && (isDebugRelevant() ? d->isDebug == referenceABI.isDebug() : true) ; }
AbstractInjector::Ptr defaultInjectorForLaunch(const ProbeABI &abi) { #if defined(Q_OS_MAC) if (abi.majorQtVersion() >= 5 && abi.minorQtVersion() >= 4) return createInjector(QLatin1String("preload")); return findFirstWorkingInjector(QStringList() << QLatin1String("lldb") << QLatin1String("gdb")); #elif defined(Q_OS_UNIX) Q_UNUSED(abi); return createInjector(QLatin1String("preload")); #else Q_UNUSED(abi); return createInjector(QLatin1String("windll")); #endif }
ProbeABI ProbeABIDetector::detectAbiForQtCore(const QString &path) const { if (path.isEmpty()) return ProbeABI(); // try to find the version ProbeABI abi = qtVersionFromFileName(path); if (!abi.hasQtVersion()) abi = qtVersionFromExec(path); // TODO: architecture detection fallback without elf.h? const QString arch = archFromELF(path); abi.setArchitecture(arch); return abi; }
int ProbeABIModel::indexOfBestMatchingABI(const ProbeABI& targetABI) const { if (!targetABI.isValid()) return -1; const ProbeABI bestMatchingABI = ProbeFinder::findBestMatchingABI(targetABI, m_abis); return m_abis.indexOf(bestMatchingABI); }
void testProbeABICompat() { #ifndef Q_OS_WIN const ProbeABI targetABI = ProbeABI::fromString(QStringLiteral("qt5_2-x86_64")); const ProbeABI probeABI = ProbeABI::fromString(QStringLiteral("qt5_1-x86_64")); #if defined(Q_OS_MAC) const bool debugAbiMatters = true; #else const bool debugAbiMatters = false; #endif const bool compilerAbiMatters = false; #else const ProbeABI targetABI = ProbeABI::fromString(QStringLiteral("qt5_2-MSVC-140-x86_64")); const ProbeABI probeABI = ProbeABI::fromString(QStringLiteral("qt5_1-MSVC-140-x86_64")); const bool debugAbiMatters = true; const bool compilerAbiMatters = true; #endif // full match, or same major version and older probe QVERIFY(targetABI.isCompatible(targetABI)); QVERIFY(targetABI.isCompatible(probeABI)); // incompatible // newer minor version probe QVERIFY(!probeABI.isCompatible(targetABI)); // different major version ProbeABI incompatABI(probeABI); incompatABI.setQtVersion(4, 8); QVERIFY(!targetABI.isCompatible(incompatABI)); QVERIFY(!incompatABI.isCompatible(targetABI)); // different architecture incompatABI = targetABI; incompatABI.setArchitecture(QStringLiteral("i686")); QVERIFY(!targetABI.isCompatible(incompatABI)); // different debug/release mode incompatABI = targetABI; incompatABI.setIsDebug(true); QCOMPARE(targetABI.isCompatible(incompatABI), !debugAbiMatters); // different compiler incompatABI = targetABI; incompatABI.setCompiler(QStringLiteral("Clang")); QCOMPARE(targetABI.isCompatible(incompatABI), !compilerAbiMatters); }
static ProbeABI qtVersionFromExec(const QString &path) { ProbeABI abi; // yep, you can actually execute QtCore.so... QProcess proc; proc.setReadChannelMode(QProcess::SeparateChannels); proc.setReadChannel(QProcess::StandardOutput); proc.start(path); proc.waitForFinished(); const QByteArray line = proc.readLine(); const int pos = line.lastIndexOf(' '); const QList<QByteArray> version = line.mid(pos).split('.'); if (version.size() < 3) return abi; abi.setQtVersion(version.at(0).toInt(), version.at(1).toInt()); return abi; }
ProbeABI findBestMatchingABI(const ProbeABI &targetABI, const QVector<ProbeABI> &availableABIs) { QVector<ProbeABI> compatABIs; foreach (const ProbeABI &abi, availableABIs) { if (targetABI.isCompatible(abi)) compatABIs.push_back(abi); } if (compatABIs.isEmpty()) return ProbeABI(); std::sort(compatABIs.begin(), compatABIs.end()); return compatABIs.last(); }
QVector<ProbeABI> listProbeABIs() { QVector<ProbeABI> abis; const QDir dir(Paths::probePath(QString())); #if defined(GAMMARAY_INSTALL_QT_LAYOUT) const QString filter = QStringLiteral("*gammaray_probe*"); foreach (const QFileInfo &abiId, dir.entryInfoList(QStringList(filter), QDir::Files)) { // OSX has broken QLibrary::isLibrary() - QTBUG-50446 if (!QLibrary::isLibrary(abiId.fileName()) && !abiId.fileName().endsWith(Paths::libraryExtension(), Qt::CaseInsensitive)) continue; const ProbeABI abi = ProbeABI::fromString(abiId.baseName().section(QStringLiteral("-"), 1)); if (abi.isValid()) abis.push_back(abi); } #else foreach (const QString &abiId, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { const ProbeABI abi = ProbeABI::fromString(abiId); if (abi.isValid()) abis.push_back(abi); } #endif return abis; }
ProbeABI ProbeABI::fromString(const QString &id) { QStringList idParts = id.split('-'); if (idParts.size() < 2) return ProbeABI(); int index = 0; ProbeABI abi; // version static QRegExp versionRegExp("^qt(\\d+)\\_(\\d+)$"); if (versionRegExp.indexIn(idParts.value(index++)) != 0) return ProbeABI(); abi.setQtVersion(versionRegExp.cap(1).toInt(), versionRegExp.cap(2).toInt()); // compiler #ifdef Q_OS_WIN abi.setCompiler(idParts.value(index++)); if (abi.isVersionRelevant()) abi.setCompilerVersion(idParts.value(index++)); #endif if (idParts.size() != index + 1) return ProbeABI(); // architecture / debug/release const QString postfix = QStringLiteral(GAMMARAY_DEBUG_POSTFIX); QString arch = idParts.value(index); if (!postfix.isEmpty()) { if (arch.endsWith(postfix, Qt::CaseInsensitive)) { arch.chop(postfix.length()); if (abi.isDebugRelevant()) abi.setIsDebug(true); } } abi.setArchitecture(arch); return abi; }
void testToString() { QFETCH(QString, id); QFETCH(int, majorVersion); QFETCH(int, minorVersion); QFETCH(bool, isDebug); QFETCH(QString, arch); QFETCH(QString, compiler); QFETCH(QString, compilerVersion); ProbeABI abi; abi.setQtVersion(majorVersion, minorVersion); abi.setIsDebug(isDebug); abi.setArchitecture(arch); abi.setCompiler(compiler); abi.setCompilerVersion(compilerVersion); QCOMPARE(abi.id(), id); }
ProbeABI ProbeABI::fromString(const QString& id) { QStringList idParts = id.split('-'); if (idParts.size() < 2) return ProbeABI(); int index = 0; ProbeABI abi; // version static QRegExp versionRegExp("^qt(\\d+)\\.(\\d+)$"); if (versionRegExp.indexIn(idParts.value(index++)) != 0) return ProbeABI(); abi.setQtVersion(versionRegExp.cap(1).toInt(), versionRegExp.cap(2).toInt()); // compiler #ifdef Q_OS_WIN abi.setCompiler(idParts.value(index++)); #endif // debug/release if (abi.isDebugRelevant()) { if (idParts.size() <= index) return ProbeABI(); const QString s = idParts.value(index++); if (s != "release" && s != "debug") return ProbeABI(); abi.setIsDebug(s == "debug"); } // architecture if (idParts.size() != index + 1) return ProbeABI(); abi.setArchitecture(idParts.value(index)); return abi; }
int main(int argc, char **argv) { QCoreApplication::setOrganizationName(QStringLiteral("KDAB")); QCoreApplication::setOrganizationDomain(QStringLiteral("kdab.com")); QCoreApplication::setApplicationName(QStringLiteral("GammaRay")); installSignalHandler(); QStringList args; args.reserve(argc); for (int i = 1; i < argc; ++i) { args.push_back(QString::fromLocal8Bit(argv[i])); } #ifdef HAVE_QT_WIDGETS QApplication app(argc, argv); // for style inspector #else QCoreApplication app(argc, argv); #endif Paths::setRelativeRootPath(GAMMARAY_INVERSE_BIN_DIR); QStringList builtInArgs = QStringList() << QStringLiteral("-style") << QStringLiteral("-stylesheet") << QStringLiteral("-graphicssystem"); LaunchOptions options; while (!args.isEmpty() && args.first().startsWith('-')) { const QString arg = args.takeFirst(); if ((arg == QLatin1String("-i") || arg == QLatin1String("--injector")) && !args.isEmpty()) { options.setInjectorType(args.takeFirst()); continue; } if ((arg == QLatin1String("-p") || arg == QLatin1String("--pid")) && !args.isEmpty()) { options.setPid( args.takeFirst().toInt() ); continue; } if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) { usage(argv[0]); return 0; } if (arg == QLatin1String("-v") || arg == QLatin1String("--version")) { out << "GammaRay version " << GAMMARAY_VERSION_STRING << endl; out << "Copyright (C) 2010-2016 Klaralvdalens Datakonsult AB, " << "a KDAB Group company, [email protected]" << endl; return 0; } if (arg == QLatin1String("--inprocess")) { options.setUiMode(LaunchOptions::InProcessUi); } if (arg == QLatin1String("--inject-only")) { options.setUiMode(LaunchOptions::NoUi); } if (arg == QLatin1String("--listen") && !args.isEmpty()) { options.setProbeSetting(QStringLiteral("ServerAddress"), urlFromUserInput(args.takeFirst()).toString()); } if ( arg == QLatin1String("--no-listen")) { options.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), false); options.setUiMode(LaunchOptions::InProcessUi); } if ( arg == QLatin1String("--list-probes")) { foreach( const ProbeABI &abi, ProbeFinder::listProbeABIs()) out << abi.id() << " (" << abi.displayString() << ")" << endl; return 0; } if ( arg == QLatin1String("--probe") && !args.isEmpty()) { const ProbeABI abi = ProbeABI::fromString(args.takeFirst()); if (!abi.isValid()) { out << "Invalid probe ABI specified, see --list-probes for valid ones." << endl; return 1; } if (ProbeFinder::findProbe(QStringLiteral(GAMMARAY_PROBE_BASENAME), abi).isEmpty()) { out << abi.id() << "is not a known probe, see --list-probes." << endl; return 1; } options.setProbeABI(abi); } if ( arg == QLatin1String("--connect") && !args.isEmpty()) { const QUrl url = urlFromUserInput(args.takeFirst()); ClientLauncher client; client.launch(url); client.waitForFinished(); return 0; } // debug/test options if (arg == QLatin1String("-filtertest")) { qputenv("GAMMARAY_TEST_FILTER", "1"); } if (arg == QLatin1String("-unittest")) { qputenv("GAMMARAY_UNITTEST", "1"); } if (arg == QLatin1String("-modeltest")) { qputenv("GAMMARAY_MODELTEST", "1"); } // built-in arguments of QApp, could be meant for us if we are showing the launcher window foreach (const QString &builtInArg, builtInArgs) { if (arg == builtInArg && !args.isEmpty()) { args.takeFirst(); } } }
bool ProbeABI::operator<(const ProbeABI& rhs) const { if (majorQtVersion() == rhs.majorQtVersion()) return minorQtVersion() < rhs.minorQtVersion(); return majorQtVersion() < rhs.majorQtVersion(); }
int main(int argc, char **argv) { QCoreApplication::setOrganizationName(QStringLiteral("KDAB")); QCoreApplication::setOrganizationDomain(QStringLiteral("kdab.com")); QCoreApplication::setApplicationName(QStringLiteral("GammaRay")); installSignalHandler(); QStringList args; args.reserve(argc); for (int i = 1; i < argc; ++i) args.push_back(QString::fromLocal8Bit(argv[i])); #ifndef GAMMARAY_CORE_ONLY_LAUNCHER QApplication app(argc, argv); // for style inspector #else QCoreApplication app(argc, argv); #endif Paths::setRelativeRootPath(GAMMARAY_INVERSE_BIN_DIR); QStringList builtInArgs = QStringList() << QStringLiteral("-style") << QStringLiteral("-stylesheet") << QStringLiteral("-graphicssystem"); LaunchOptions options; while (!args.isEmpty() && args.first().startsWith('-')) { const QString arg = args.takeFirst(); if ((arg == QLatin1String("-i") || arg == QLatin1String("--injector")) && !args.isEmpty()) { options.setInjectorType(args.takeFirst()); continue; } if ((arg == QLatin1String("-o") || arg == QLatin1String("--injector-override")) && !args.isEmpty()) { options.setInjectorTypeExecutableOverride(args.takeFirst()); continue; } if ((arg == QLatin1String("-p") || arg == QLatin1String("--pid")) && !args.isEmpty()) { options.setPid(args.takeFirst().toInt()); continue; } if (arg == QLatin1String("-h") || arg == QLatin1String("--help")) { usage(argv[0]); return 0; } if (arg == QLatin1String("-v") || arg == QLatin1String("--version")) { out << "GammaRay version " << GAMMARAY_VERSION_STRING << endl; out << "Copyright (C) 2010-2016 Klaralvdalens Datakonsult AB, " << "a KDAB Group company, [email protected]" << endl; out << "Protocol version " << Protocol::version() << endl; out << "Broadcast version " << Protocol::broadcastFormatVersion() << endl; return 0; } if (arg == QLatin1String("--inprocess")) options.setUiMode(LaunchOptions::InProcessUi); if (arg == QLatin1String("--inject-only")) options.setUiMode(LaunchOptions::NoUi); if (arg == QLatin1String("--listen") && !args.isEmpty()) options.setProbeSetting(QStringLiteral("ServerAddress"), urlFromUserInput(args.takeFirst()).toString()); if (arg == QLatin1String("--no-listen")) { options.setProbeSetting(QStringLiteral("RemoteAccessEnabled"), false); options.setUiMode(LaunchOptions::InProcessUi); } if (arg == QLatin1String("--list-probes")) { foreach (const ProbeABI &abi, ProbeFinder::listProbeABIs()) out << abi.id() << " (" << abi.displayString() << ")" << endl; return 0; } if (arg == QLatin1String("--probe") && !args.isEmpty()) { const ProbeABI abi = ProbeABI::fromString(args.takeFirst()); if (!abi.isValid()) { out << "Invalid probe ABI specified, see --list-probes for valid ones." << endl; return 1; } if (ProbeFinder::findProbe(abi).isEmpty()) { out << abi.id() << "is not a known probe, see --list-probes." << endl; return 1; } options.setProbeABI(abi); } if (arg == QLatin1String("--connect") && !args.isEmpty()) { const QUrl url = urlFromUserInput(args.takeFirst()); ClientLauncher client; client.launch(url); client.waitForFinished(); return 0; } if (arg == QLatin1String("--self-test")) { SelfTest selfTest; #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QObject::connect(&selfTest, &SelfTest::information, [](const QString &msg) { out << msg << endl; }); QObject::connect(&selfTest, &SelfTest::error, [](const QString &msg) { err << "Error: " << msg << endl; }); #endif if (args.isEmpty() || args.first().startsWith('-')) return selfTest.checkEverything() ? 0 : 1; const auto injectorType = args.takeFirst(); return selfTest.checkInjector(injectorType) ? 0 : 1; } // debug/test options if (arg == QLatin1String("-filtertest")) qputenv("GAMMARAY_TEST_FILTER", "1"); if (arg == QLatin1String("-unittest")) qputenv("GAMMARAY_UNITTEST", "1"); // built-in arguments of QApp, could be meant for us if we are showing the launcher window foreach (const QString &builtInArg, builtInArgs) { if (arg == builtInArg && !args.isEmpty()) args.takeFirst(); } }