/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "debuggerrunner.h" #include "debuggerruncontrolfactory.h" #include "debuggeractions.h" #include "debuggerinternalconstants.h" #include "debuggercore.h" #include "debuggerengine.h" #include "debuggermainwindow.h" #include "debuggerplugin.h" #include "debuggerstringutils.h" #include "debuggerstartparameters.h" #include "lldb/lldbenginehost.h" #include "debuggertooltipmanager.h" #include "qml/qmlengine.h" #ifdef Q_OS_WIN # include "peutils.h" # include #endif #include #include #include #include #include #include #include #include // For LocalApplication* #include #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; using namespace Debugger::Internal; enum { debug = 0 }; namespace Debugger { namespace Internal { bool isCdbEngineEnabled(); // Check the configuration page bool checkCdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check); DebuggerEngine *createCdbEngine(const DebuggerStartParameters &sp, DebuggerEngine *masterEngine, QString *error); bool checkGdbConfiguration(const DebuggerStartParameters &sp, ConfigurationCheck *check); DebuggerEngine *createGdbEngine(const DebuggerStartParameters &sp, DebuggerEngine *masterEngine); DebuggerEngine *createScriptEngine(const DebuggerStartParameters &sp); DebuggerEngine *createPdbEngine(const DebuggerStartParameters &sp); QmlEngine *createQmlEngine(const DebuggerStartParameters &sp, DebuggerEngine *masterEngine); DebuggerEngine *createQmlCppEngine(const DebuggerStartParameters &sp, DebuggerEngineType slaveEngineType, QString *errorMessage); DebuggerEngine *createLldbEngine(const DebuggerStartParameters &sp); extern QString msgNoBinaryForToolChain(const Abi &abi); static const char *engineTypeName(DebuggerEngineType et) { switch (et) { case Debugger::NoEngineType: break; case Debugger::GdbEngineType: return "Gdb engine"; case Debugger::ScriptEngineType: return "Script engine"; case Debugger::CdbEngineType: return "Cdb engine"; case Debugger::PdbEngineType: return "Pdb engine"; case Debugger::QmlEngineType: return "QML engine"; case Debugger::QmlCppEngineType: return "QML C++ engine"; case Debugger::LldbEngineType: return "LLDB engine"; case Debugger::AllEngineTypes: break; } return "No engine"; } static inline QString engineTypeNames(const QList &l) { QString rc; foreach (DebuggerEngineType et, l) { if (!rc.isEmpty()) rc.append(QLatin1String(", ")); rc += QLatin1String(engineTypeName(et)); } return rc; } static QString msgEngineNotAvailable(const char *engine) { return DebuggerPlugin::tr("The application requires the debugger engine '%1', " "which is disabled.").arg(_(engine)); } static inline QString msgEngineNotAvailable(DebuggerEngineType et) { return msgEngineNotAvailable(engineTypeName(et)); } //////////////////////////////////////////////////////////////////////// // // DebuggerRunControlPrivate // //////////////////////////////////////////////////////////////////////// class DebuggerRunControlPrivate { public: explicit DebuggerRunControlPrivate(DebuggerRunControl *parent, RunConfiguration *runConfiguration); DebuggerEngineType engineForExecutable(unsigned enabledEngineTypes, const QString &executable); DebuggerEngineType engineForMode(unsigned enabledEngineTypes, DebuggerStartMode mode); public: DebuggerRunControl *q; DebuggerEngine *m_engine; const QWeakPointer m_myRunConfiguration; bool m_running; }; DebuggerRunControlPrivate::DebuggerRunControlPrivate(DebuggerRunControl *parent, RunConfiguration *runConfiguration) : q(parent) , m_engine(0) , m_myRunConfiguration(runConfiguration) , m_running(false) { } } // namespace Internal DebuggerRunControl::DebuggerRunControl(RunConfiguration *runConfiguration, const DebuggerStartParameters &sp, const QPair &masterSlaveEngineTypes) : RunControl(runConfiguration, Constants::DEBUGMODE), d(new DebuggerRunControlPrivate(this, runConfiguration)) { connect(this, SIGNAL(finished()), SLOT(handleFinished())); // Create the engine. Could arguably be moved to the factory, but // we still have a derived S60DebugControl. Should rarely fail, though. QString errorMessage; d->m_engine = masterSlaveEngineTypes.first == QmlCppEngineType ? createQmlCppEngine(sp, masterSlaveEngineTypes.second, &errorMessage) : DebuggerRunControlFactory::createEngine(masterSlaveEngineTypes.first, sp, 0, &errorMessage); if (d->m_engine) { DebuggerToolTipManager::instance()->registerEngine(d->m_engine); } else { debuggingFinished(); Core::ICore::instance()->showWarningWithOptions(DebuggerRunControl::tr("Debugger"), errorMessage); } } DebuggerRunControl::~DebuggerRunControl() { disconnect(); if (DebuggerEngine *engine = d->m_engine) { d->m_engine = 0; engine->disconnect(); delete engine; } } const DebuggerStartParameters &DebuggerRunControl::startParameters() const { QTC_ASSERT(d->m_engine, return *(new DebuggerStartParameters())); return d->m_engine->startParameters(); } QString DebuggerRunControl::displayName() const { QTC_ASSERT(d->m_engine, return QString()); return d->m_engine->startParameters().displayName; } QIcon DebuggerRunControl::icon() const { return QIcon(ProjectExplorer::Constants::ICON_DEBUG_SMALL); } void DebuggerRunControl::setCustomEnvironment(Utils::Environment env) { QTC_ASSERT(d->m_engine, return); d->m_engine->startParameters().environment = env; } void DebuggerRunControl::start() { QTC_ASSERT(d->m_engine, return); // User canceled input dialog asking for executable when working on library project. if (d->m_engine->startParameters().startMode == StartInternal && d->m_engine->startParameters().executable.isEmpty()) { appendMessage(tr("No executable specified.\n"), Utils::ErrorMessageFormat); emit started(); emit finished(); return; } foreach (const BreakpointId &id, debuggerCore()->breakHandler()->allBreakpointIds()) { if (!d->m_engine->acceptsBreakpoint(id)) { debuggerCore()->showMessage(DebuggerPlugin::tr("Some breakpoints cannot be handled by the current debugger, and will be ignored."), LogWarning); int result = QMessageBox::warning(debuggerCore()->mainWindow(), DebuggerPlugin::tr("Warning"), DebuggerPlugin::tr("Some breakpoints cannot be handled by the debugger, and will be ignored. Do you want to continue?"), QMessageBox::Ok | QMessageBox::Cancel); if (result == QMessageBox::Cancel) { emit started(); emit finished(); return; } break; } } debuggerCore()->runControlStarted(d->m_engine); // We might get a synchronous startFailed() notification on Windows, // when launching the process fails. Emit a proper finished() sequence. emit started(); d->m_running = true; d->m_engine->startDebugger(this); if (d->m_running) appendMessage(tr("Debugging starts\n"), Utils::NormalMessageFormat); } void DebuggerRunControl::startFailed() { appendMessage(tr("Debugging has failed\n"), Utils::NormalMessageFormat); d->m_running = false; emit finished(); d->m_engine->handleStartFailed(); } void DebuggerRunControl::handleFinished() { appendMessage(tr("Debugging has finished\n"), Utils::NormalMessageFormat); if (d->m_engine) d->m_engine->handleFinished(); debuggerCore()->runControlFinished(d->m_engine); } void DebuggerRunControl::showMessage(const QString &msg, int channel) { switch (channel) { case AppOutput: appendMessage(msg, Utils::StdOutFormatSameLine); break; case AppError: appendMessage(msg, Utils::StdErrFormatSameLine); break; case AppStuff: appendMessage(msg, Utils::NormalMessageFormat); break; } } bool DebuggerRunControl::promptToStop(bool *optionalPrompt) const { QTC_ASSERT(isRunning(), return true;) if (optionalPrompt && !*optionalPrompt) return true; const QString question = tr("A debugging session is still in progress. " "Terminating the session in the current" " state can leave the target in an inconsistent state." " Would you still like to terminate it?"); return showPromptToStopDialog(tr("Close Debugging Session"), question, QString(), QString(), optionalPrompt); } RunControl::StopResult DebuggerRunControl::stop() { QTC_ASSERT(d->m_engine, return StoppedSynchronously); d->m_engine->quitDebugger(); return AsynchronousStop; } void DebuggerRunControl::debuggingFinished() { d->m_running = false; emit finished(); } bool DebuggerRunControl::isRunning() const { return d->m_running; } DebuggerEngine *DebuggerRunControl::engine() { QTC_ASSERT(d->m_engine, /**/); return d->m_engine; } RunConfiguration *DebuggerRunControl::runConfiguration() const { return d->m_myRunConfiguration.data(); } //////////////////////////////////////////////////////////////////////// // // Engine detection logic: Detection functions depending on tool chain, binary, // etc. Return a list of possible engines (order of prefererence) without // consideration of configuration, etc. // //////////////////////////////////////////////////////////////////////// static QList enginesForToolChain(const Abi &toolChain) { QList result; switch (toolChain.binaryFormat()) { case Abi::ElfFormat: case Abi::MachOFormat: result.push_back(LldbEngineType); result.push_back(GdbEngineType); break; case Abi::PEFormat: if (toolChain.osFlavor() == Abi::WindowsMSysFlavor) { result.push_back(GdbEngineType); result.push_back(CdbEngineType); } else { result.push_back(CdbEngineType); result.push_back(GdbEngineType); } break; case Abi::RuntimeQmlFormat: result.push_back(QmlEngineType); break; default: break; } return result; } static inline QList enginesForScriptExecutables(const QString &executable) { QList result; if (executable.endsWith(_(".js"))) { result.push_back(ScriptEngineType); } else if (executable.endsWith(_(".py"))) { result.push_back(PdbEngineType); } return result; } static QList enginesForExecutable(const QString &executable) { QList result = enginesForScriptExecutables(executable); if (!result.isEmpty()) return result; #ifdef Q_OS_WIN // A remote executable? if (!executable.endsWith(_(".exe"), Qt::CaseInsensitive)) { result.push_back(GdbEngineType); return result; } // If a file has PDB files, it has been compiled by VS. QStringList pdbFiles; QString errorMessage; if (getPDBFiles(executable, &pdbFiles, &errorMessage) && !pdbFiles.isEmpty()) { result.push_back(CdbEngineType); result.push_back(GdbEngineType); return result; } // Fixme: Gdb should only be preferred if MinGW can positively be detected. result.push_back(GdbEngineType); result.push_back(CdbEngineType); #else result.push_back(LldbEngineType); result.push_back(GdbEngineType); #endif return result; } // Debugger type for mode. static QList enginesForMode(DebuggerStartMode startMode, bool hardConstraintsOnly) { QList result; switch (startMode) { case NoStartMode: break; case StartInternal: case StartExternal: case AttachExternal: if (!hardConstraintsOnly) { #ifdef Q_OS_WIN result.push_back(CdbEngineType); // Preferably Windows debugger for attaching locally. #endif result.push_back(GdbEngineType); } break; case AttachCore: case StartRemoteGdb: result.push_back(GdbEngineType); break; case AttachToRemote: if (!hardConstraintsOnly) { #ifdef Q_OS_WIN result.push_back(CdbEngineType); #endif result.push_back(GdbEngineType); } break; case AttachCrashedExternal: result.push_back(CdbEngineType); // Only CDB can do this break; case StartRemoteEngine: // FIXME: Unclear IPC override. Someone please have a better idea. // For now thats the only supported IPC engine. result.push_back(LldbEngineType); break; } return result; } // Engine detection logic: Call all detection functions in order. static QList engineTypes(const DebuggerStartParameters &sp) { // Script executables and certain start modes are 'hard constraints'. QList result = enginesForScriptExecutables(sp.executable); if (!result.isEmpty()) return result; result = enginesForMode(sp.startMode, true); if (!result.isEmpty()) return result; // 'hard constraints' done (with the exception of QML ABI checked here), // further try to restrict available engines. if (sp.toolChainAbi.isValid()) { result = enginesForToolChain(sp.toolChainAbi); if (!result.isEmpty()) return result; } // FIXME: 1 of 3 testing hacks. if (sp.processArgs.startsWith(__("@tcf@ "))) { result.push_back(GdbEngineType); return result; } if (sp.startMode != AttachToRemote && !sp.executable.isEmpty()) result = enginesForExecutable(sp.executable); if (!result.isEmpty()) return result; result = enginesForMode(sp.startMode, false); return result; } // Engine detection logic: ConfigurationCheck. ConfigurationCheck::ConfigurationCheck() : masterSlaveEngineTypes(NoEngineType, NoEngineType) { } ConfigurationCheck::operator bool() const { return errorMessage.isEmpty() && errorDetails.isEmpty() && masterSlaveEngineTypes.first != NoEngineType; } QString ConfigurationCheck::errorDetailsString() const { return errorDetails.join(QLatin1String("\n\n")); } // Convenience helper to check whether an engine is enabled and configured // correctly. static inline bool canUseEngine(DebuggerEngineType et, const DebuggerStartParameters &sp, unsigned cmdLineEnabledEngines, ConfigurationCheck *result) { // Enabled? if ((et & cmdLineEnabledEngines) == 0) { result->errorDetails.push_back(DebuggerPlugin::tr("The debugger engine '%1' is disabled."). arg(engineTypeName(et))); return false; } // Configured. switch (et) { case CdbEngineType: return checkCdbConfiguration(sp, result); case GdbEngineType: return checkGdbConfiguration(sp, result); default: break; } return true; } /*! \fn Debugger::ConfigurationCheck Debugger::checkDebugConfiguration(const DebuggerStartParameters &sp) This is the master engine detection function that returns the engine types for a given set of start parameters and checks their configuration. */ DEBUGGER_EXPORT ConfigurationCheck checkDebugConfiguration(const DebuggerStartParameters &sp) { ConfigurationCheck result; const unsigned activeLangs = debuggerCore()->activeLanguages(); const bool qmlLanguage = activeLangs & QmlLanguage; const bool cppLanguage = activeLangs & CppLanguage; if (debug) qDebug().nospace() << "checkDebugConfiguration " << sp.toolChainAbi.toString() << " Start mode=" << sp.startMode << " Executable=" << sp.executable << " Debugger command=" << sp.debuggerCommand; // Get all applicable types. QList requiredTypes; if (qmlLanguage && !cppLanguage) { requiredTypes.push_back(QmlEngineType); } else { requiredTypes = engineTypes(sp); } if (requiredTypes.isEmpty()) { result.errorMessage = QLatin1String("Internal error: Unable to determine debugger engine type for this configuration"); return result; } if (debug) qDebug() << " Required: " << engineTypeNames(requiredTypes); // Filter out disables types, command line + current settings. unsigned cmdLineEnabledEngines = debuggerCore()->enabledEngines(); #ifdef WITH_LLDB if (!Core::ICore::instance()->settings()->value(QLatin1String("LLDB/enabled")).toBool()) cmdLineEnabledEngines &= ~LldbEngineType; #else cmdLineEnabledEngines &= ~LldbEngineType; #endif DebuggerEngineType usableType = NoEngineType; QList unavailableTypes; foreach (DebuggerEngineType et, requiredTypes) { if (canUseEngine(et, sp, cmdLineEnabledEngines, &result)) { usableType = et; break; } else { unavailableTypes.push_back(et); } } if (usableType == NoEngineType) { if (requiredTypes.size() == 1) { result.errorMessage = DebuggerPlugin::tr( "The debugger engine '%1' required for debugging binaries of the type '%2'" " is not configured correctly."). arg(QLatin1String(engineTypeName(requiredTypes.front())), sp.toolChainAbi.toString()); } else { result.errorMessage = DebuggerPlugin::tr( "None of the debugger engines '%1' capable of debugging binaries of the type '%2'" " is configured correctly."). arg(engineTypeNames(requiredTypes), sp.toolChainAbi.toString()); } return result; } if (debug) qDebug() << "Configured engine: " << engineTypeName(usableType); // Inform verbosely about MinGW-gdb/CDB fallbacks. Do not complain about LLDB, for now. if (!result.errorDetails.isEmpty() && unavailableTypes.count(LldbEngineType) != unavailableTypes.size()) { const QString msg = DebuggerPlugin::tr( "The preferred debugger engine for debugging binaries of type '%1' is not available.\n" "The debugger engine '%2' will be used as a fallback.\nDetails: %3"). arg(sp.toolChainAbi.toString(), engineTypeName(usableType), result.errorDetails.join(QString(QLatin1Char('\n')))); debuggerCore()->showMessage(msg, LogWarning); showMessageBox(QMessageBox::Warning, DebuggerPlugin::tr("Warning"), msg); } // Anything left: Happy. result.errorMessage.clear(); result.errorDetails.clear(); if (qmlLanguage && cppLanguage) { result.masterSlaveEngineTypes.first = QmlCppEngineType; result.masterSlaveEngineTypes.second = usableType; } else { result.masterSlaveEngineTypes.first = usableType; } if (debug) qDebug() << engineTypeName(result.masterSlaveEngineTypes.first) << engineTypeName(result.masterSlaveEngineTypes.second); return result; } //////////////////////////////////////////////////////////////////////// // // DebuggerRunControlFactory // //////////////////////////////////////////////////////////////////////// // A factory to create DebuggerRunControls DebuggerRunControlFactory::DebuggerRunControlFactory(QObject *parent, unsigned enabledEngines) : IRunControlFactory(parent), m_enabledEngines(enabledEngines) {} bool DebuggerRunControlFactory::canRun(RunConfiguration *runConfiguration, const QString &mode) const { // return mode == ProjectExplorer::Constants::DEBUGMODE; return (mode == Constants::DEBUGMODE || mode == Constants::DEBUGMODE2) && qobject_cast(runConfiguration); } QString DebuggerRunControlFactory::displayName() const { return DebuggerRunControl::tr("Debug"); } // Find Qt installation by running qmake static inline QString findQtInstallPath(const QString &qmakePath) { QProcess proc; QStringList args; args.append(_("-query")); args.append(_("QT_INSTALL_HEADERS")); proc.start(qmakePath, args); if (!proc.waitForStarted()) { qWarning("%s: Cannot start '%s': %s", Q_FUNC_INFO, qPrintable(qmakePath), qPrintable(proc.errorString())); return QString(); } proc.closeWriteChannel(); if (!proc.waitForFinished()) { Utils::SynchronousProcess::stopProcess(proc); qWarning("%s: Timeout running '%s'.", Q_FUNC_INFO, qPrintable(qmakePath)); return QString(); } if (proc.exitStatus() != QProcess::NormalExit) { qWarning("%s: '%s' crashed.", Q_FUNC_INFO, qPrintable(qmakePath)); return QString(); } const QByteArray ba = proc.readAllStandardOutput().trimmed(); QDir dir(QString::fromLocal8Bit(ba)); if (dir.exists() && dir.cdUp()) return dir.absolutePath(); return QString(); } static DebuggerStartParameters localStartParameters(RunConfiguration *runConfiguration) { DebuggerStartParameters sp; QTC_ASSERT(runConfiguration, return sp); LocalApplicationRunConfiguration *rc = qobject_cast(runConfiguration); QTC_ASSERT(rc, return sp); sp.startMode = StartInternal; sp.environment = rc->environment(); sp.workingDirectory = rc->workingDirectory(); #if defined(Q_OS_WIN) // Work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch' ...) sp.workingDirectory = Utils::normalizePathName(sp.workingDirectory); #endif sp.executable = rc->executable(); sp.processArgs = rc->commandLineArguments(); sp.toolChainAbi = rc->abi(); sp.useTerminal = rc->runMode() == LocalApplicationRunConfiguration::Console; sp.dumperLibrary = rc->dumperLibrary(); sp.dumperLibraryLocations = rc->dumperLibraryLocations(); if (const ProjectExplorer::Target *target = runConfiguration->target()) { if (QByteArray(target->metaObject()->className()).contains("Qt4")) { const QString qmake = Utils::BuildableHelperLibrary::findSystemQt(sp.environment); if (!qmake.isEmpty()) sp.qtInstallPath = findQtInstallPath(qmake); } if (const ProjectExplorer::Project *project = target->project()) { sp.projectSourceDirectory = project->projectDirectory(); if (const ProjectExplorer::BuildConfiguration *buildConfig = target->activeBuildConfiguration()) { sp.projectBuildDirectory = buildConfig->buildDirectory(); if (const ProjectExplorer::ToolChain *tc = buildConfig->toolChain()) sp.debuggerCommand = tc->debuggerCommand(); } sp.projectSourceFiles = project->files(Project::ExcludeGeneratedFiles); } } if (debuggerCore()->isActiveDebugLanguage(QmlLanguage)) { sp.qmlServerAddress = _("127.0.0.1"); sp.qmlServerPort = runConfiguration->qmlDebugServerPort(); // Makes sure that all bindings go through the JavaScript engine, so that // breakpoints are actually hit! const QString optimizerKey = _("QML_DISABLE_OPTIMIZER"); if (!sp.environment.hasKey(optimizerKey)) { sp.environment.set(optimizerKey, _("1")); } Utils::QtcProcess::addArg(&sp.processArgs, _("-qmljsdebugger=port:") + QString::number(sp.qmlServerPort)); } // FIXME: If it's not yet build this will be empty and not filled // when rebuild as the runConfiguration is not stored and therefore // cannot be used to retrieve the dumper location. //qDebug() << "DUMPER: " << sp.dumperLibrary << sp.dumperLibraryLocations; sp.displayName = rc->displayName(); return sp; } RunControl *DebuggerRunControlFactory::create (RunConfiguration *runConfiguration, const QString &mode) { QTC_ASSERT(mode == Constants::DEBUGMODE || mode == Constants::DEBUGMODE2, return 0); DebuggerStartParameters sp = localStartParameters(runConfiguration); if (mode == Constants::DEBUGMODE2) sp.breakOnMain = true; return create(sp, runConfiguration); } RunConfigWidget *DebuggerRunControlFactory::createConfigurationWidget (RunConfiguration *runConfiguration) { // NBS TODO: Add GDB-specific configuration widget Q_UNUSED(runConfiguration) return 0; } DebuggerRunControl *DebuggerRunControlFactory::create (const DebuggerStartParameters &sp, RunConfiguration *runConfiguration) { const ConfigurationCheck check = checkDebugConfiguration(sp); if (!check) { //appendMessage(errorMessage, true); Core::ICore::instance()->showWarningWithOptions(DebuggerRunControl::tr("Debugger"), check.errorMessage, check.errorDetailsString(), check.settingsCategory, check.settingsPage); return 0; } return new DebuggerRunControl(runConfiguration, sp, check.masterSlaveEngineTypes); } DebuggerEngine *DebuggerRunControlFactory::createEngine (DebuggerEngineType et, const DebuggerStartParameters &sp, DebuggerEngine *masterEngine, QString *errorMessage) { switch (et) { case GdbEngineType: return createGdbEngine(sp, masterEngine); case ScriptEngineType: return createScriptEngine(sp); case CdbEngineType: return createCdbEngine(sp, masterEngine, errorMessage); case PdbEngineType: return createPdbEngine(sp); case QmlEngineType: return createQmlEngine(sp, masterEngine); case LldbEngineType: return createLldbEngine(sp); default: break; } *errorMessage = DebuggerRunControl::tr("Unable to create a debugger engine of the type '%1'"). arg(_(engineTypeName(et))); return 0; } } // namespace Debugger