From 3af81887845fce2b7b320057e661e15cf700e93b Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 19 Jan 2011 10:25:32 +0100 Subject: [PATCH] Debugger[CDB]: Use Qt Creator console stub instead of CDB console. As the latter immediately closes after the debuggee quits. Handle pressing Ctrl like DebugBreak(), switch to thread 0. --- src/plugins/debugger/cdb/cdbengine.cpp | 205 ++++++++++++++++++++++++------ src/plugins/debugger/cdb/cdbengine.h | 11 ++ src/plugins/debugger/shared/dbgwinutils.h | 23 ++-- 3 files changed, 188 insertions(+), 51 deletions(-) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 8ad1c17f48..9ce10a8a45 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,8 @@ #include #include #include +#include +#include #ifdef Q_OS_WIN # include @@ -89,9 +92,9 @@ enum { debugWatches = 0 }; enum { debugBreakpoints = 0 }; #if 0 -# define STATE_DEBUG(func, line, notifyFunc) qDebug("%s at %s:%d", notifyFunc, func, line); +# define STATE_DEBUG(state, func, line, notifyFunc) qDebug("%s in %s at %s:%d", notifyFunc, stateName(state), func, line); #else -# define STATE_DEBUG(func, line, notifyFunc) +# define STATE_DEBUG(state, func, line, notifyFunc) #endif /* CdbEngine version 2: Run the CDB process on pipes and parse its output. @@ -117,7 +120,19 @@ enum { debugBreakpoints = 0 }; * then invokes a callback with a CdbBuiltinCommand structure. * 3) postExtensionCommand(): Run a command provided by the extension producing * one-line output and invoke a callback with a CdbExtensionCommand structure - * (output is potentially split up in chunks). */ + * (output is potentially split up in chunks). + * + * Startup sequence: + * [Console: The console stub launches the process. On process startup, + * launchCDB() is called with AttachExternal. + * setupEngine() calls launchCDB() with the startparameters. The debuggee + * runs into the initial breakpoint (session idle). EngineSetupOk is + * notified (inferior still stopped). setupInferior() is then called + * which does breakpoint synchronization and issues the extension 'pid' + * command to obtain the inferior pid. handlePid() notifies notifyInferiorSetupOk. + * runEngine() is then called which issues 'g' to continue the inferior. + * Shutdown mostly uses notifyEngineSpontaneousShutdown() as cdb just quits + * when the inferior exits (except attach modes). */ using namespace ProjectExplorer; @@ -154,6 +169,19 @@ Q_DECLARE_METATYPE(Debugger::Internal::SourceLocationCookie) namespace Debugger { namespace Internal { +static inline bool isConsole(const DebuggerStartParameters &sp) +{ return (sp.startMode == StartInternal || sp.startMode == StartExternal) && sp.useTerminal; } + +static QMessageBox + *nonModalMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text) +{ + QMessageBox *mb = new QMessageBox(icon, title, text, QMessageBox::Ok, + debuggerCore()->mainWindow()); + mb->setAttribute(Qt::WA_DeleteOnClose); + mb->show(); + return mb; +} + // Base data structure for command queue entries with callback struct CdbCommandBase { @@ -340,6 +368,7 @@ CdbEngine::CdbEngine(const DebuggerStartParameters &sp, m_creatorExtPrefix("|"), m_tokenPrefix(""), m_options(options), + m_effectiveStartMode(NoStartMode), m_inferiorPid(0), m_accessible(false), m_specialStopMode(NoSpecialStop), @@ -417,20 +446,6 @@ void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEd } } -void CdbEngine::setupEngine() -{ - // Nag to add symbol server - if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(), - &(m_options->symbolPaths))) - m_options->toSettings(Core::ICore::instance()->settings()); - - QString errorMessage; - if (!doSetupEngine(&errorMessage)) { // Start engine which will run until initial breakpoint - showMessage(errorMessage, LogError); - notifyEngineSetupFailed(); - } -} - // Determine full path to the CDB extension library. QString CdbEngine::extensionLibraryName(bool is64Bit) { @@ -479,9 +494,104 @@ int CdbEngine::elapsedLogTime() const return delta; } -bool CdbEngine::doSetupEngine(QString *errorMessage) +// Start the console stub with the sub process. Continue in consoleStubProcessStarted. +bool CdbEngine::startConsole(const DebuggerStartParameters &sp, QString *errorMessage) +{ + if (debug) + qDebug("startConsole %s", qPrintable(sp.executable)); + m_consoleStub.reset(new Utils::ConsoleProcess); + m_consoleStub->setMode(Utils::ConsoleProcess::Suspend); + connect(m_consoleStub.data(), SIGNAL(processMessage(QString, bool)), + SLOT(consoleStubMessage(QString, bool))); + connect(m_consoleStub.data(), SIGNAL(processStarted()), + SLOT(consoleStubProcessStarted())); + connect(m_consoleStub.data(), SIGNAL(wrapperStopped()), + SLOT(consoleStubExited())); + m_consoleStub->setWorkingDirectory(sp.workingDirectory); + m_consoleStub->setEnvironment(sp.environment); + if (!m_consoleStub->start(sp.executable, sp.processArgs)) { + *errorMessage = tr("The console process '%1' could not be started.").arg(sp.executable); + return false; + } + return true; +} + +void CdbEngine::consoleStubMessage(const QString &msg, bool isError) { + if (debug) + qDebug("consoleStubProcessMessage() in %s error=%d %s", stateName(state()), isError, qPrintable(msg)); + if (isError) { + if (state() == EngineSetupRequested) { + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed") + notifyEngineSetupFailed(); + } else { + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll") + notifyEngineIll(); + } + nonModalMessageBox(QMessageBox::Critical, tr("Debugger Error"), msg); + } else { + showMessage(msg, AppOutput); + } +} + +void CdbEngine::consoleStubProcessStarted() +{ + if (debug) + qDebug("consoleStubProcessStarted() PID=%lld", m_consoleStub->applicationPID()); + // Attach to console process. + DebuggerStartParameters attachParameters = startParameters(); + attachParameters.executable.clear(); + attachParameters.processArgs.clear(); + attachParameters.attachPID = m_consoleStub->applicationPID(); + attachParameters.startMode = AttachExternal; + showMessage(QString::fromLatin1("Attaching to %1...").arg(attachParameters.attachPID), LogMisc); + QString errorMessage; + if (!launchCDB(attachParameters, &errorMessage)) { + showMessage(errorMessage, LogError); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupFailed") + notifyEngineSetupFailed(); + } +} + +void CdbEngine::consoleStubExited() +{ +} + +void CdbEngine::setupEngine() +{ + if (debug) + qDebug(">setupEngine"); + // Nag to add symbol server + if (CdbSymbolPathListEditor::promptToAddSymbolServer(CdbOptions::settingsGroup(), + &(m_options->symbolPaths))) + m_options->toSettings(Core::ICore::instance()->settings()); + m_logTime.start(); + QString errorMessage; + // Console: Launch the stub with the suspended application and attach to it + // CDB in theory has a command line option '-2' that launches a + // console, too, but that immediately closes when the debuggee quits. + // Use the Creator stub instead. + const DebuggerStartParameters &sp = startParameters(); + const bool launchConsole = isConsole(sp); + m_effectiveStartMode = launchConsole ? AttachExternal : sp.startMode; + const bool ok = launchConsole ? + startConsole(startParameters(), &errorMessage) : + launchCDB(startParameters(), &errorMessage); + if (debug) + qDebug("breakEvents) postCommand(QByteArray("sxe ") + breakEvent.toAscii(), 0); postCommand("g", 0); @@ -598,19 +711,20 @@ void CdbEngine::shutdownInferior() if (!isCdbProcessRunning()) { // Direct launch: Terminated with process. if (debug) qDebug("notifyInferiorShutdownOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk") notifyInferiorShutdownOk(); return; } if (!canInterruptInferior() || commandsPending()) { + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFailed") notifyInferiorShutdownFailed(); return; } if (m_accessible) { - if (startParameters().startMode == AttachExternal || startParameters().startMode == AttachCrashedExternal) + if (m_effectiveStartMode == AttachExternal || m_effectiveStartMode == AttachCrashedExternal) detachDebugger(); - if (debug) - qDebug("notifyInferiorShutdownOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownOk") notifyInferiorShutdownOk(); } else { interruptInferior(); // Calls us again @@ -634,8 +748,7 @@ void CdbEngine::shutdownEngine() commandsPending()); if (!isCdbProcessRunning()) { // Direct launch: Terminated with process. - if (debug) - qDebug("notifyEngineShutdownOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk") notifyEngineShutdownOk(); return; } @@ -646,10 +759,10 @@ void CdbEngine::shutdownEngine() // Go for kill if there are commands pending. if (m_accessible && !commandsPending()) { // detach: Wait for debugger to finish. - if (startParameters().startMode == AttachExternal) + if (m_effectiveStartMode == AttachExternal) detachDebugger(); // Remote requires a bit more force to quit. - if (startParameters().startMode == AttachToRemote) { + if (m_effectiveStartMode == AttachToRemote) { postCommand(m_extensionCommandPrefixBA + "shutdownex", 0); postCommand("qq", 0); } else { @@ -689,15 +802,14 @@ void CdbEngine::processFinished() if (crashed) { if (debug) qDebug("notifyEngineIll"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineIll") notifyEngineIll(); } else { - if (debug) - qDebug("notifyEngineShutdownOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineShutdownOk") notifyEngineShutdownOk(); } } else { - if (debug) - qDebug("notifyEngineSpontaneousShutdown"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSpontaneousShutdown") notifyEngineSpontaneousShutdown(); } } @@ -806,18 +918,21 @@ void CdbEngine::executeStep() postCommand(QByteArray("t"), 0); // Step into-> t (trace) if (!m_operateByInstruction) m_sourceStepInto = true; // See explanation at handleStackTrace(). + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); } void CdbEngine::executeStepOut() { postCommand(QByteArray("gu"), 0); // Step out-> gu (go up) + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); } void CdbEngine::executeNext() { postCommand(QByteArray("p"), 0); // Step over -> p + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); } @@ -833,6 +948,7 @@ void CdbEngine::executeNextI() void CdbEngine::continueInferior() { + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); doContinueInferior(); } @@ -844,7 +960,7 @@ void CdbEngine::doContinueInferior() bool CdbEngine::canInterruptInferior() const { - return startParameters().startMode != AttachToRemote; + return m_effectiveStartMode != AttachToRemote; } void CdbEngine::interruptInferior() @@ -853,8 +969,11 @@ void CdbEngine::interruptInferior() doInterruptInferior(NoSpecialStop); } else { showMessage(tr("Interrupting is not possible in remote sessions."), LogError); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk") notifyInferiorStopOk(); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk") notifyInferiorRunOk(); } } @@ -1264,10 +1383,12 @@ void CdbEngine::handlePid(const CdbExtensionCommandPtr &reply) if (reply->success) { m_inferiorPid = reply->reply.toUInt(); showMessage(QString::fromLatin1("Inferior pid: %1.").arg(m_inferiorPid), LogMisc); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupOk") notifyInferiorSetupOk(); } else { showMessage(QString::fromLatin1("Failed to determine inferior pid: %1"). arg(QLatin1String(reply->errorMessage)), LogError); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed") notifyInferiorSetupFailed(); } } @@ -1488,6 +1609,11 @@ unsigned CdbEngine::examineStopReason(const QByteArray &messageIn, return StopIgnoreContinue|StopReportLog; } } + if (exception.exceptionCode == winExceptionCtrlPressed) { + // Detect interruption by pressing Ctrl in a console and force a switch to thread 0. + *message = msgInterrupted(); + return StopReportStatusMessage|StopNotifyStop|StopInArtificialThread; + } if (isDebuggerWinException(exception.exceptionCode)) { unsigned rc = StopReportStatusMessage|StopNotifyStop; // Detect interruption by DebugBreak() and force a switch to thread 0. @@ -1537,8 +1663,7 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA) } if (state() == EngineSetupRequested) { // Temporary stop at beginning - if (debug) - qDebug("notifyEngineSetupOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk") notifyEngineSetupOk(); return; } @@ -1562,12 +1687,10 @@ void CdbEngine::handleSessionIdle(const QByteArray &messageBA) // Notify about state and send off command sequence to get stack, etc. if (stopFlags & StopNotifyStop) { if (state() == InferiorStopRequested) { - if (debug) - qDebug("notifyInferiorStopOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorStopOk") notifyInferiorStopOk(); } else { - if (debug) - qDebug("notifyInferiorSpontaneousStop"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSpontaneousStop") notifyInferiorSpontaneousStop(); } // Start sequence to get all relevant data. @@ -1626,8 +1749,7 @@ void CdbEngine::handleSessionInaccessible(unsigned long cdbExState) case EngineSetupRequested: break; case EngineRunRequested: - if (debug) - qDebug("notifyEngineRunAndInferiorRunOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineRunAndInferiorRunOk") notifyEngineRunAndInferiorRunOk(); break; case InferiorRunOk: @@ -1641,8 +1763,7 @@ void CdbEngine::handleSessionInaccessible(unsigned long cdbExState) } break; case InferiorRunRequested: - if (debug) - qDebug("notifyInferiorRunOk"); + STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunOk") notifyInferiorRunOk(); resetLocation(); break; @@ -1944,6 +2065,8 @@ bool CdbEngine::acceptsBreakpoint(BreakpointId id) const void CdbEngine::attemptBreakpointSynchronization() { + if (debug) + qDebug("attemptBreakpointSynchronization in %s", stateName(state())); // Check if there is anything to be done at all. BreakHandler *handler = breakHandler(); // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only? diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index caf74fad62..8d35d40853 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -42,6 +42,9 @@ #include #include +namespace Utils { +class ConsoleProcess; +} namespace Debugger { namespace Internal { @@ -150,6 +153,10 @@ private slots: void postCommandSequence(unsigned mask); void operateByInstructionTriggered(bool); + void consoleStubMessage(const QString &, bool); + void consoleStubProcessStarted(); + void consoleStubExited(); + private: enum SpecialStopMode { @@ -158,11 +165,13 @@ private: SpecialStopGetWidgetAt }; + bool startConsole(const DebuggerStartParameters &sp, QString *errorMessage); unsigned examineStopReason(const QByteArray &messageIn, QString *message, QString *exceptionBoxMessage); bool commandsPending() const; void handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message); bool doSetupEngine(QString *errorMessage); + bool launchCDB(const DebuggerStartParameters &sp, QString *errorMessage); void handleSessionAccessible(unsigned long cdbExState); void handleSessionInaccessible(unsigned long cdbExState); void handleSessionIdle(const QByteArray &message); @@ -202,6 +211,8 @@ private: const OptionsPtr m_options; QProcess m_process; + QScopedPointer m_consoleStub; + DebuggerStartMode m_effectiveStartMode; QByteArray m_outputBuffer; unsigned long m_inferiorPid; // Debugger accessible (expecting commands) diff --git a/src/plugins/debugger/shared/dbgwinutils.h b/src/plugins/debugger/shared/dbgwinutils.h index 8327cfd18c..7b4f3ba5b2 100644 --- a/src/plugins/debugger/shared/dbgwinutils.h +++ b/src/plugins/debugger/shared/dbgwinutils.h @@ -67,16 +67,19 @@ QString winNormalizeFileName(const QString &f); bool isWinProcessBeingDebugged(unsigned long pid); // Special exception codes. -enum { winExceptionCppException = 0xe06d7363, - winExceptionStartupCompleteTrap = 0x406d1388, - winExceptionRpcServerUnavailable = 0x6ba, - winExceptionRpcServerInvalid = 0x6a6, - winExceptionDllNotFound = 0xc0000135, - winExceptionDllEntryPointNoFound = 0xc0000139, - winExceptionDllInitFailed = 0xc0000142, - winExceptionMissingSystemFile = 0xc0000143, - winExceptionAppInitFailed = 0xc0000143, - winExceptionWX86Breakpoint = 0x4000001f +enum +{ + winExceptionCppException = 0xe06d7363, + winExceptionStartupCompleteTrap = 0x406d1388, + winExceptionRpcServerUnavailable = 0x6ba, + winExceptionRpcServerInvalid = 0x6a6, + winExceptionDllNotFound = 0xc0000135, + winExceptionDllEntryPointNoFound = 0xc0000139, + winExceptionDllInitFailed = 0xc0000142, + winExceptionMissingSystemFile = 0xc0000143, + winExceptionAppInitFailed = 0xc0000143, + winExceptionWX86Breakpoint = 0x4000001f, + winExceptionCtrlPressed = 0x40010005 }; // Format windows Exception -- 2.11.0