Commit 7ffc3733 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger/CDB: Fix stack view when attaching to crashed app.



The timer for event-delivery time-out checking when attaching to a
crashed process in the case of not being spawned by the handler
(such as via single-application remote command) triggered too
early. Add a 'moduleLoaded()' signal to CoreEngine and trigger
from there (due to lack of a proper "engine up/attached"
notification.
Emit moduleLoaded() from the first timed-out debug event handler when
modules no longer change.
Reviewed-by: default avatarRobert Loehning <robert.loehning@nokia.com>
parent 7f2dea7c
......@@ -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
......
......@@ -102,8 +102,9 @@ public:
void updateCodeLevel();
public slots:
private slots:
void handleDebugEvent();
void slotModulesLoaded();
public:
const QSharedPointer<CdbOptions> m_options;
......
......@@ -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;
}
......
......@@ -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, ..."
......
......@@ -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
......
......@@ -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
......@@ -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
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment