diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 8736769dec83df22b229751877896cd0b6dc4129..ec7c9f34d4194f359f3b100303b5dce1fc5d651a 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -135,6 +135,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *manager, m_mode(AttachCore) { connect(this, SIGNAL(watchTimerDebugEvent()), this, SLOT(handleDebugEvent())); + connect(this, SIGNAL(modulesLoaded()), this, SLOT(slotModulesLoaded())); } bool CdbDebugEnginePrivate::init(QString *errorMessage) @@ -475,19 +476,14 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6 } // Attaching to crashed: This handshake (signalling an event) is required for // the exception to be delivered to the debugger + // Also, see special handling in slotModulesLoaded(). if (m_mode == AttachCrashedExternal) { const QString crashParameter = manager()->startParameters()->crashParameter; if (!crashParameter.isEmpty()) { ULONG64 evtNr = crashParameter.toULongLong(); const HRESULT hr = interfaces().debugControl->SetNotifyEventHandle(evtNr); - // Unless QtCreator is spawned by the debugger and inherits the handles, - // the event handling does not work reliably - // (that is, the crash event is not delivered). - if (SUCCEEDED(hr)) { - QTimer::singleShot(0, m_engine, SLOT(slotBreakAttachToCrashed())); - } else { + if (FAILED(hr)) m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(CdbCore::msgComFailed("SetNotifyEventHandle", hr))); - } } } m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__); @@ -877,6 +873,16 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage) return rc; } +void CdbDebugEnginePrivate::slotModulesLoaded() +{ + // Attaching to crashed windows processes: Unless QtCreator is + // spawned by the debug handler and inherits the handles, + // the event handling does not work reliably (that is, the crash + // event is not delivered). In that case, force a break + if (m_mode == AttachCrashedExternal && m_engine->state() != InferiorStopped) + QTimer::singleShot(10, m_engine, SLOT(slotBreakAttachToCrashed())); +} + void CdbDebugEngine::slotBreakAttachToCrashed() { // Force a break when attaching to crashed process (if Creator was not spawned diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 1db92d231cd93ebe375af4e9131dcc65219bbc46..a4ab5d1acd75c599892265242d484359fb4b5287 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -102,8 +102,9 @@ public: void updateCodeLevel(); -public slots: +private slots: void handleDebugEvent(); + void slotModulesLoaded(); public: const QSharedPointer<CdbOptions> m_options; diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp index 2251fb0052296410f13ac30734e01621c258efbf..e6b8fc50e59decad5d5add3d0480dd29c5874f92 100644 --- a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp +++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp @@ -50,10 +50,9 @@ CdbDebugEventCallback::CdbDebugEventCallback(CdbDebugEngine* dbg) : STDMETHODIMP CdbDebugEventCallback::GetInterestMask(THIS_ __out PULONG mask) { *mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_EXIT_PROCESS - | DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE | DEBUG_EVENT_CREATE_THREAD | DEBUG_EVENT_EXIT_THREAD | DEBUG_EVENT_BREAKPOINT - | DEBUG_EVENT_EXCEPTION; + | DEBUG_EVENT_EXCEPTION | baseInterestMask(); return S_OK; } @@ -168,13 +167,13 @@ STDMETHODIMP CdbDebugEventCallback::LoadModule( { Q_UNUSED(ImageFileHandle) Q_UNUSED(BaseOffset) - Q_UNUSED(ModuleName) Q_UNUSED(ModuleSize) Q_UNUSED(ImageName) Q_UNUSED(CheckSum) Q_UNUSED(TimeDateStamp) if (debugCDB > 1) qDebug() << Q_FUNC_INFO << ModuleName; + handleModuleLoad(); m_pEngine->m_d->handleModuleLoad(QString::fromUtf16(reinterpret_cast<const ushort *>(ModuleName))); return S_OK; } @@ -189,6 +188,7 @@ STDMETHODIMP CdbDebugEventCallback::UnloadModule( Q_UNUSED(BaseOffset) if (debugCDB > 1) qDebug() << Q_FUNC_INFO << ImageBaseName; + handleModuleUnload(); m_pEngine->m_d->updateModules(); return S_OK; } @@ -216,7 +216,7 @@ CdbExceptionLoggerEventCallback::CdbExceptionLoggerEventCallback(int logChannel, STDMETHODIMP CdbExceptionLoggerEventCallback::GetInterestMask(THIS_ __out PULONG mask) { - *mask = DEBUG_EVENT_EXCEPTION; + *mask = DEBUG_EVENT_EXCEPTION | baseInterestMask(); return S_OK; } diff --git a/src/plugins/debugger/cdb/coreengine.cpp b/src/plugins/debugger/cdb/coreengine.cpp index bfab160023b65f172e5596635df8472425818c60..6705906759a55e3c009adf5fd32fda5c1c76e76e 100644 --- a/src/plugins/debugger/cdb/coreengine.cpp +++ b/src/plugins/debugger/cdb/coreengine.cpp @@ -224,7 +224,10 @@ bool DebuggerEngineLibrary::init(const QString &path, // ------ Engine CoreEngine::CoreEngine(QObject *parent) : QObject(parent), - m_watchTimer(-1) + m_watchTimer(-1), + m_moduleCount(0), + m_lastTimerModuleCount(0), + m_modulesLoadedEmitted(true) { } @@ -323,9 +326,12 @@ CoreEngine::DebugOutputBasePtr CoreEngine::DebugEventCallbackBasePtr CoreEngine::setDebugEventCallback(const DebugEventCallbackBasePtr &e) { + // Keep the module count. + const unsigned oldModuleCount = moduleCount(); const CoreEngine::DebugEventCallbackBasePtr old = m_debugEventCallback; m_debugEventCallback = e; m_cif.debugClient->SetEventCallbacksWide(m_debugEventCallback.data()); + setModuleCount(oldModuleCount); return old; } @@ -366,13 +372,24 @@ void CoreEngine::timerEvent(QTimerEvent* te) // (such as step over,etc). if (te->timerId() != m_watchTimer) return; - switch (waitForEvent(1)) { case S_OK: killWatchTimer(); emit watchTimerDebugEvent(); break; case S_FALSE: + // Detect startup (all modules loaded) if the module + // count no longer changes in a time-out. + if (!m_modulesLoadedEmitted) { + const int newModuleCount = moduleCount(); + if (newModuleCount && newModuleCount == m_lastTimerModuleCount) { + m_modulesLoadedEmitted = true; + emit modulesLoaded(); + } else { + m_lastTimerModuleCount = newModuleCount; + } + } + break; case E_PENDING: case E_FAIL: break; @@ -382,12 +399,20 @@ void CoreEngine::timerEvent(QTimerEvent* te) } } +void CoreEngine::resetModuleLoadTimer() +{ + m_lastTimerModuleCount = 0; + setModuleCount(0); + m_modulesLoadedEmitted = false; +} + bool CoreEngine::startDebuggerWithExecutable(const QString &workingDirectory, const QString &filename, const QStringList &args, const QStringList &envList, QString *errorMessage) { + resetModuleLoadTimer(); DEBUG_CREATE_PROCESS_OPTIONS dbgopts; memset(&dbgopts, 0, sizeof(dbgopts)); dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; @@ -437,7 +462,8 @@ bool CoreEngine::startAttachDebugger(qint64 pid, bool suppressInitialBreakPoint, QString *errorMessage) { - // Need to attrach invasively, otherwise, no notification signals + resetModuleLoadTimer(); + // Need to attach invasively, otherwise, no notification signals // for for CreateProcess/ExitProcess occur. // Initial breakpoint occur: // 1) Desired: When attaching to a crashed process @@ -865,6 +891,20 @@ bool CoreEngine::setInterrupt(QString *errorMessage) return true; } +// Module count is normally kept in the event callback. +// Should we have none, we do the book-keeping ourselves. +unsigned CoreEngine::moduleCount() const +{ + return m_debugEventCallback.isNull() ? m_moduleCount : m_debugEventCallback->moduleCount(); +} + +void CoreEngine::setModuleCount(unsigned m) +{ + m_moduleCount = m; + if (!m_debugEventCallback.isNull()) + m_debugEventCallback->setModuleCount(m); +} + // ------------- DEBUG_VALUE formatting helpers // format an array of integers as "0x323, 0x2322, ..." diff --git a/src/plugins/debugger/cdb/coreengine.h b/src/plugins/debugger/cdb/coreengine.h index 4391823a15c53d83c364e6424b1a6127f11cab1c..eff131807dcccf46ba17836c97f53f5dff22d631 100644 --- a/src/plugins/debugger/cdb/coreengine.h +++ b/src/plugins/debugger/cdb/coreengine.h @@ -149,19 +149,33 @@ public: static bool autoDetectPath(QString *outPath, QStringList *checkedDirectories = 0); + unsigned moduleCount() const; + signals: void watchTimerDebugEvent(); + // Emitted in the first time-out of the event handler in which + // the number of modules no longer changes. Can be used as a + // "startup" signal due to lack of a reliable "startup" detection + // feature of the engine. + void modulesLoaded(); + protected: virtual void timerEvent(QTimerEvent* te); private: + void setModuleCount(unsigned m); + void resetModuleLoadTimer(); + ComInterfaces m_cif; DebugOutputBasePtr m_debugOutput; DebugEventCallbackBasePtr m_debugEventCallback; QString m_dbengDLL; QString m_baseImagePath; int m_watchTimer; + unsigned m_moduleCount; + unsigned m_lastTimerModuleCount; + bool m_modulesLoadedEmitted; }; // Utility messages diff --git a/src/plugins/debugger/cdb/debugeventcallbackbase.cpp b/src/plugins/debugger/cdb/debugeventcallbackbase.cpp index 9cad433aa114065de4f992f3781b72d87dc1eb8c..7ef52846e5f047c7292c6dfbbeff2495e27b9607 100644 --- a/src/plugins/debugger/cdb/debugeventcallbackbase.cpp +++ b/src/plugins/debugger/cdb/debugeventcallbackbase.cpp @@ -140,6 +140,7 @@ STDMETHODIMP DebugEventCallbackBase::LoadModule( __in ULONG /* TimeDateStamp */ ) { + handleModuleLoad(); return S_OK; } @@ -149,6 +150,7 @@ STDMETHODIMP DebugEventCallbackBase::UnloadModule( __in ULONG64 /* BaseOffset */ ) { + handleModuleUnload(); return S_OK; } @@ -204,6 +206,33 @@ IDebugEventCallbacksWide *DebugEventCallbackBase::getEventCallback(CIDebugClient return 0; } +void DebugEventCallbackBase::handleModuleLoad() +{ + m_moduleCount++; +} + +void DebugEventCallbackBase::handleModuleUnload() +{ + m_moduleCount--; +} + +unsigned DebugEventCallbackBase::moduleCount() const +{ + return m_moduleCount; +} + +void DebugEventCallbackBase::setModuleCount(unsigned m) +{ + m_moduleCount = m; +} + +ULONG DebugEventCallbackBase::baseInterestMask() const +{ + return DEBUG_EVENT_LOAD_MODULE | DEBUG_EVENT_UNLOAD_MODULE; +} + +// ----------- EventCallbackRedirector + EventCallbackRedirector::EventCallbackRedirector(CoreEngine *engine, const DebugEventCallbackBasePtr &cb) : m_engine(engine), @@ -216,4 +245,5 @@ EventCallbackRedirector::~EventCallbackRedirector() m_engine->setDebugEventCallback(m_oldCallback); } + } // namespace CdbCore diff --git a/src/plugins/debugger/cdb/debugeventcallbackbase.h b/src/plugins/debugger/cdb/debugeventcallbackbase.h index cb7360fbebc03b4fbd265fbb99cac4a1913b1c2f..6d8e589c780eabc8cfafcddcb6ac854d344ce357 100644 --- a/src/plugins/debugger/cdb/debugeventcallbackbase.h +++ b/src/plugins/debugger/cdb/debugeventcallbackbase.h @@ -40,7 +40,13 @@ class CoreEngine; // Base class for event callbacks that takes care // Active X magic. Provides base implementations with -// the exception of GetInterestMask +// the exception of GetInterestMask(). The base class +// needs to do some book-keeping on the modules loaded to +// be able to detect the startup/completed attach of a +// debuggee (see CoreEngine::modulesLoaded()). +// So, the interest mask must be at least baseInterestMask() +// and handleModuleLoad/Unload must be called from derived +// classes when overwriting the handlers. class DebugEventCallbackBase : public IDebugEventCallbacksWide { protected: @@ -104,6 +110,7 @@ public: __in ULONG ExitCode ); + // Call handleModuleLoad() when reimplementing this STDMETHOD(LoadModule)( THIS_ __in ULONG64 ImageFileHandle, @@ -115,6 +122,7 @@ public: __in ULONG TimeDateStamp ); + // Call handleModuleUnload() when reimplementing this STDMETHOD(UnloadModule)( THIS_ __in_opt PCWSTR ImageBaseName, @@ -152,6 +160,17 @@ public: static IDebugEventCallbacksWide *getEventCallback(CIDebugClient *clnt); + + unsigned moduleCount() const; + void setModuleCount(unsigned m); + +protected: + void handleModuleLoad(); + void handleModuleUnload(); + ULONG baseInterestMask() const; + +private: + unsigned m_moduleCount; }; // Utility class to temporarily redirect events to another handler