Commit c70f968f authored by Friedemann Kleint's avatar Friedemann Kleint

CDB: Fix step into, improve multithread dumping, exception logging

Use new call syntax of 6.11. for dumper call loading. Execute
Dumpers in a single thread (current) if at all possible (not
in some WaitFor or artificial break thread). Show one more
frame in threads view if it is FastCallReturn. Fix step into
(ignore one event), log some more exceptions. Generally log
exceptions to the debugger windows to be able to see stuff
like DLL missing, etc.
parent 1110b622
...@@ -1080,7 +1080,7 @@ ...@@ -1080,7 +1080,7 @@
\l{http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx}{32-bit} \l{http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx}{32-bit}
or or
\l{http://www.microsoft.com/whdc/devtools/debugging/install64bit.Mspx}{64-bit} \l{http://www.microsoft.com/whdc/devtools/debugging/install64bit.Mspx}{64-bit}
package (Version 6.10 for the 32-bit or the 64-bit version of Qt Creator, respectively), package (Version 6.11.1.404 for the 32-bit or the 64-bit version of Qt Creator, respectively),
which is freely available for download from the which is freely available for download from the
\l{http://msdn.microsoft.com/en-us/default.aspx} \l{http://msdn.microsoft.com/en-us/default.aspx}
{Microsoft Developer Network}. {Microsoft Developer Network}.
......
...@@ -302,6 +302,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *manager, ...@@ -302,6 +302,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *manager,
m_dumper(new CdbDumperHelper(manager, &m_cif)), m_dumper(new CdbDumperHelper(manager, &m_cif)),
m_currentThreadId(-1), m_currentThreadId(-1),
m_eventThreadId(-1), m_eventThreadId(-1),
m_interrupted(false),
m_watchTimer(-1), m_watchTimer(-1),
m_debugEventCallBack(engine), m_debugEventCallBack(engine),
m_engine(engine), m_engine(engine),
...@@ -445,6 +446,7 @@ void CdbDebugEnginePrivate::clearForRun() ...@@ -445,6 +446,7 @@ void CdbDebugEnginePrivate::clearForRun()
m_breakEventMode = BreakEventHandle; m_breakEventMode = BreakEventHandle;
m_eventThreadId = -1; m_eventThreadId = -1;
m_interrupted = false;
cleanStackTrace(); cleanStackTrace();
} }
...@@ -1026,6 +1028,9 @@ bool CdbDebugEngine::step(unsigned long executionStatus) ...@@ -1026,6 +1028,9 @@ bool CdbDebugEngine::step(unsigned long executionStatus)
warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, msgDebuggerCommandFailed(command, hr))); warning(msgStepFailed(executionStatus, m_d->m_currentThreadId, msgDebuggerCommandFailed(command, hr)));
} }
if (success) { if (success) {
// Oddity: Step into will first break at the calling function. Ignore
if (executionStatus == DEBUG_STATUS_STEP_INTO || executionStatus == DEBUG_STATUS_REVERSE_STEP_INTO)
m_d->m_breakEventMode = CdbDebugEnginePrivate::BreakEventIgnoreOnce;
startWatchTimer(); startWatchTimer();
setState(InferiorRunning, Q_FUNC_INFO, __LINE__); setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
} else { } else {
...@@ -1169,7 +1174,9 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage) ...@@ -1169,7 +1174,9 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
qDebug() << Q_FUNC_INFO << "\n ex=" << executionStatus; qDebug() << Q_FUNC_INFO << "\n ex=" << executionStatus;
} }
if (!DebugBreakProcess(m_hDebuggeeProcess)) { if (DebugBreakProcess(m_hDebuggeeProcess)) {
m_interrupted = true;
} else {
*errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Utils::winErrorMessage(GetLastError())); *errorMessage = QString::fromLatin1("DebugBreakProcess failed: %1").arg(Utils::winErrorMessage(GetLastError()));
return false; return false;
} }
...@@ -1704,12 +1711,34 @@ ULONG CdbDebugEnginePrivate::updateThreadList() ...@@ -1704,12 +1711,34 @@ ULONG CdbDebugEnginePrivate::updateThreadList()
QList<ThreadData> threads; QList<ThreadData> threads;
ULONG currentThreadId; ULONG currentThreadId;
QString errorMessage; QString errorMessage;
// When interrupting, an artifical thread with a breakpoint is created.
if (!CdbStackTraceContext::getThreads(m_cif, true, &threads, &currentThreadId, &errorMessage)) if (!CdbStackTraceContext::getThreads(m_cif, true, &threads, &currentThreadId, &errorMessage))
m_engine->warning(errorMessage); m_engine->warning(errorMessage);
manager()->threadsHandler()->setThreads(threads); manager()->threadsHandler()->setThreads(threads);
return currentThreadId; return currentThreadId;
} }
// Figure out the thread to run the dumpers in (see notes on.
// CdbDumperHelper). Avoid the artifical threads created by interrupt
// and threads that are in waitFor().
// A stricter version could only use the thread if it is the event
// thread of a step or breakpoint hit (see CdbDebugEnginePrivate::m_interrupted).
static inline unsigned long dumperThreadId(const QList<StackFrame> &frames,
unsigned long currentThread)
{
if (frames.empty())
return CdbDumperHelper::InvalidDumperCallThread;
if (frames.at(0).function == QLatin1String(CdbStackTraceContext::winFuncDebugBreakPoint))
return CdbDumperHelper::InvalidDumperCallThread;
const int waitCheckDepth = qMin(frames.size(), 5);
static const QString waitForPrefix = QLatin1String(CdbStackTraceContext::winFuncWaitForPrefix);
for (int f = 0; f < waitCheckDepth; f++)
if (frames.at(f).function.startsWith(waitForPrefix))
return CdbDumperHelper::InvalidDumperCallThread;
return currentThread;
}
void CdbDebugEnginePrivate::updateStackTrace() void CdbDebugEnginePrivate::updateStackTrace()
{ {
if (debugCDB) if (debugCDB)
...@@ -1750,11 +1779,19 @@ void CdbDebugEnginePrivate::updateStackTrace() ...@@ -1750,11 +1779,19 @@ void CdbDebugEnginePrivate::updateStackTrace()
CdbDebugEngine::tr("Thread %1: No debug information available (%2).").arg(m_currentThreadId).arg(topFunction); CdbDebugEngine::tr("Thread %1: No debug information available (%2).").arg(m_currentThreadId).arg(topFunction);
m_engine->warning(msg); m_engine->warning(msg);
} }
// Set up dumper with a thread (or invalid)
const unsigned long dumperThread = dumperThreadId(stackFrames, m_currentThreadId);
if (debugCDB)
qDebug() << "updateStackTrace() current: " << m_currentThreadId << " dumper=" << dumperThread;
m_dumper->setDumperCallThread(dumperThread);
// Display frames
manager()->stackHandler()->setFrames(stackFrames); manager()->stackHandler()->setFrames(stackFrames);
m_firstActivatedFrame = true; m_firstActivatedFrame = true;
if (current >= 0) { if (current >= 0) {
manager()->stackHandler()->setCurrentIndex(current); manager()->stackHandler()->setCurrentIndex(current);
// First time : repaint
if (m_dumper->isEnabled() && m_dumper->state() != CdbDumperHelper::Initialized)
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
m_engine->activateFrame(current); m_engine->activateFrame(current);
} }
manager()->watchHandler()->updateWatchers(); manager()->watchHandler()->updateWatchers();
......
...@@ -156,6 +156,7 @@ struct CdbDebugEnginePrivate ...@@ -156,6 +156,7 @@ struct CdbDebugEnginePrivate
const QSharedPointer<CdbOptions> m_options; const QSharedPointer<CdbOptions> m_options;
HANDLE m_hDebuggeeProcess; HANDLE m_hDebuggeeProcess;
HANDLE m_hDebuggeeThread; HANDLE m_hDebuggeeThread;
bool m_interrupted;
int m_currentThreadId; int m_currentThreadId;
int m_eventThreadId; int m_eventThreadId;
HandleBreakEventMode m_breakEventMode; HandleBreakEventMode m_breakEventMode;
......
...@@ -248,6 +248,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception( ...@@ -248,6 +248,7 @@ STDMETHODIMP CdbDebugEventCallback::Exception(
if (debugCDB) if (debugCDB)
qDebug() << Q_FUNC_INFO << "\nex=" << Exception->ExceptionCode << " fatal=" << fatal << msg; qDebug() << Q_FUNC_INFO << "\nex=" << Exception->ExceptionCode << " fatal=" << fatal << msg;
m_pEngine->manager()->showApplicationOutput(msg); m_pEngine->manager()->showApplicationOutput(msg);
m_pEngine->manager()->showDebuggerOutput(LogMisc, msg);
if (fatal) if (fatal)
m_pEngine->m_d->notifyCrashed(); m_pEngine->m_d->notifyCrashed();
return S_OK; return S_OK;
......
...@@ -41,8 +41,10 @@ ...@@ -41,8 +41,10 @@
#include <QtCore/QRegExp> #include <QtCore/QRegExp>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QTextStream> #include <QtCore/QTextStream>
#include <QtCore/QTime>
enum { loadDebug = 0 }; enum { loadDebug = 0 };
enum { dumpDebug = 0 };
static const char *dumperModuleNameC = "gdbmacros"; static const char *dumperModuleNameC = "gdbmacros";
static const char *qtCoreModuleNameC = "QtCore"; static const char *qtCoreModuleNameC = "QtCore";
...@@ -158,24 +160,25 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager, ...@@ -158,24 +160,25 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager,
// We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)" // We want to call "HMODULE LoadLibraryA(LPCTSTR lpFileName)"
// (void* LoadLibraryA(char*)). However, despite providing a symbol // (void* LoadLibraryA(char*)). However, despite providing a symbol
// server, the debugger refuses to recognize it as a function. // server, the debugger refuses to recognize it as a function.
// Set up the call stack with a function of same signature (qstrdup) // Call with a prototype of 'qstrdup', as it is the same
// and change the call register to LoadLibraryA() before executing "g".
// Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some // Prepare call: Locate 'qstrdup' in the (potentially namespaced) corelib. For some
// reason, the symbol is present in QtGui as well without type information. // reason, the symbol is present in QtGui as well without type information.
QString dummyFunc = QLatin1String("*qstrdup"); QString dummyFunc = QLatin1String("*qstrdup");
if (resolveSymbol(cif->debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk) if (resolveSymbol(cif->debugSymbols, QLatin1String("QtCore[d]*4!"), &dummyFunc, errorMessage) != ResolveSymbolOk)
return false; return false;
QString callCmd = QLatin1String(".call ");
callCmd += dummyFunc; QString callCmd; {
callCmd += QLatin1String("(0x"); QTextStream str(&callCmd);
callCmd += QString::number(nameAddress, 16); str.setIntegerBase(16);
callCmd += QLatin1Char(')'); str << ".call /s " << dummyFunc << " Kernel32!LoadLibraryA(0x" << nameAddress << ')';
}
if (loadDebug)
qDebug() << "Calling" << callCmd;
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, callCmd, errorMessage))
return false; return false;
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QLatin1String("r eip=Kernel32!LoadLibraryA"), errorMessage)) // Execute current thread. This will hit a breakpoint.
return false; if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QLatin1String("~. g"), errorMessage))
// This will hit a breakpoint.
if (!CdbDebugEnginePrivate::executeDebuggerCommand(cif->debugControl, QString(QLatin1Char('g')), errorMessage))
return false; return false;
const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS); const HRESULT hr = cif->debugControl->WaitForEvent(0, waitTimeOutMS);
if (FAILED(hr)) { if (FAILED(hr)) {
...@@ -185,6 +188,14 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager, ...@@ -185,6 +188,14 @@ static bool debuggeeLoadLibrary(DebuggerManager *manager,
return true; return true;
} }
// Format a "go" in a thread
static inline QString goCommand(unsigned long threadId)
{
QString rc;
QTextStream(&rc) << '~' << threadId << " g";
return rc;
}
// ---- Load messages // ---- Load messages
static inline QString msgMethod(bool injectOrCall) static inline QString msgMethod(bool injectOrCall)
{ {
...@@ -226,7 +237,9 @@ CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager, ...@@ -226,7 +237,9 @@ CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager,
m_inBufferSize(0), m_inBufferSize(0),
m_outBufferAddress(0), m_outBufferAddress(0),
m_outBufferSize(0), m_outBufferSize(0),
m_buffer(0) m_buffer(0),
m_dumperCallThread(0),
m_goCommand(goCommand(m_dumperCallThread))
{ {
} }
...@@ -324,7 +337,7 @@ CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMess ...@@ -324,7 +337,7 @@ CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMess
bool CdbDumperHelper::ensureInitialized(QString *errorMessage) bool CdbDumperHelper::ensureInitialized(QString *errorMessage)
{ {
if (loadDebug) if (loadDebug)
qDebug() << Q_FUNC_INFO << '\n' << m_state; qDebug() << "ensureInitialized thread: " << m_dumperCallThread << " state: " << m_state;
switch (m_state) { switch (m_state) {
case Disabled: case Disabled:
...@@ -372,6 +385,9 @@ bool CdbDumperHelper::ensureInitialized(QString *errorMessage) ...@@ -372,6 +385,9 @@ bool CdbDumperHelper::ensureInitialized(QString *errorMessage)
m_manager->showDebuggerOutput(LogMisc, *errorMessage); m_manager->showDebuggerOutput(LogMisc, *errorMessage);
m_manager->showQtDumperLibraryWarning(*errorMessage); m_manager->showQtDumperLibraryWarning(*errorMessage);
} }
if (loadDebug)
qDebug() << Q_FUNC_INFO << '\n' << ok;
return ok; return ok;
} }
...@@ -451,7 +467,7 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage) ...@@ -451,7 +467,7 @@ bool CdbDumperHelper::initKnownTypes(QString *errorMessage)
*errorMessage = QtDumperHelper::msgDumperOutdated(dumperVersionRequired, m_helper.dumperVersion()); *errorMessage = QtDumperHelper::msgDumperOutdated(dumperVersionRequired, m_helper.dumperVersion());
return false; return false;
} }
if (loadDebug) if (loadDebug || dumpDebug)
qDebug() << Q_FUNC_INFO << m_helper.toString(true); qDebug() << Q_FUNC_INFO << m_helper.toString(true);
return true; return true;
} }
...@@ -492,8 +508,11 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf ...@@ -492,8 +508,11 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf
// by using 'gN' (go not handled -> pass handling to dumper __try/__catch block) // by using 'gN' (go not handled -> pass handling to dumper __try/__catch block)
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
const int oldExceptionCount = exLogger.exceptionCount(); const int oldExceptionCount = exLogger.exceptionCount();
// Go. If an exception occurs in loop 2, let the dumper handle it. // Go in current thread. If an exception occurs in loop 2,
const QString goCmd = i ? QString(QLatin1String("gN")) : QString(QLatin1Char('g')); // let the dumper handle it.
QString goCmd = m_goCommand;
if (i)
goCmd = QLatin1Char('N');
if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage)) if (!CdbDebugEnginePrivate::executeDebuggerCommand(m_cif->debugControl, goCmd, errorMessage))
return false; return false;
HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS); HRESULT hr = m_cif->debugControl->WaitForEvent(0, waitTimeOutMS);
...@@ -556,6 +575,18 @@ static inline QString msgNotHandled(const QString &type) ...@@ -556,6 +575,18 @@ static inline QString msgNotHandled(const QString &type)
CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren, CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren,
QList<WatchData> *result, QString *errorMessage) QList<WatchData> *result, QString *errorMessage)
{ {
if (dumpDebug)
qDebug() << ">dumpType() thread: " << m_dumperCallThread << " state: " << m_state << wd.type << QTime::currentTime().toString();
const CdbDumperHelper::DumpResult rc = dumpTypeI(wd, dumpChildren, result, errorMessage);
if (dumpDebug)
qDebug() << "<dumpType() state: " << m_state << wd.type << " returns " << rc << *errorMessage << QTime::currentTime().toString();
return rc;
}
CdbDumperHelper::DumpResult CdbDumperHelper::dumpTypeI(const WatchData &wd, bool dumpChildren,
QList<WatchData> *result, QString *errorMessage)
{
errorMessage->clear();
// Check failure cache and supported types // Check failure cache and supported types
if (m_state == Disabled) { if (m_state == Disabled) {
*errorMessage = QLatin1String("Dumpers are disabled"); *errorMessage = QLatin1String("Dumpers are disabled");
...@@ -570,6 +601,20 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool ...@@ -570,6 +601,20 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool
return DumpNotHandled; return DumpNotHandled;
} }
// Do we have a thread
if (m_dumperCallThread == InvalidDumperCallThread) {
*errorMessage = QString::fromLatin1("No thread to call.");
if (loadDebug)
qDebug() << *errorMessage;
return DumpNotHandled;
}
// Delay initialization as much as possible
if (isIntOrFloatType(wd.type)) {
*errorMessage = QString::fromLatin1("Unhandled POD: " ) + wd.type;
return DumpNotHandled;
}
// Ensure types are parsed and known. // Ensure types are parsed and known.
if (!ensureInitialized(errorMessage)) { if (!ensureInitialized(errorMessage)) {
*errorMessage = msgDumpFailed(wd, errorMessage); *errorMessage = msgDumpFailed(wd, errorMessage);
...@@ -722,5 +767,18 @@ bool CdbDumperHelper::runTypeSizeQuery(const QString &typeName, int *size, QStri ...@@ -722,5 +767,18 @@ bool CdbDumperHelper::runTypeSizeQuery(const QString &typeName, int *size, QStri
return true; return true;
} }
unsigned long CdbDumperHelper::dumperCallThread()
{
return m_dumperCallThread;
}
void CdbDumperHelper::setDumperCallThread(unsigned long t)
{
if (m_dumperCallThread != t) {
m_dumperCallThread = t;
m_goCommand = goCommand(m_dumperCallThread);
}
}
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger
...@@ -55,7 +55,17 @@ struct CdbComInterfaces; ...@@ -55,7 +55,17 @@ struct CdbComInterfaces;
* dumpType() is the main query function to obtain a list of WatchData from * dumpType() is the main query function to obtain a list of WatchData from
* WatchData item produced by the smbol context. * WatchData item produced by the smbol context.
* Call disable(), should the debuggee crash (as performing debuggee * Call disable(), should the debuggee crash (as performing debuggee
* calls is no longer possible, then).*/ * calls is no longer possible, then).
*
* dumperCallThread specifies the thread to use when making the calls.
* As of Debugging Tools v 6.11.1.404 (6.10.2009), calls cannot be executed
* when the current thread is in some WaitFor...() function. The call will
* then hang (regardless whether that thread or some other, non-blocking thread
* is used), and the debuggee will be in running state afterwards (causing errors
* from ReadVirtual, etc).
* The current thread can be used when stepping or a breakpoint was
* hit. When interrupting the inferior, an artifical thread is created,
* that is not usable, either. */
class CdbDumperHelper class CdbDumperHelper
{ {
...@@ -93,6 +103,10 @@ public: ...@@ -93,6 +103,10 @@ public:
inline CdbComInterfaces *comInterfaces() const { return m_cif; } inline CdbComInterfaces *comInterfaces() const { return m_cif; }
enum { InvalidDumperCallThread = 0xFFFFFFFF };
unsigned long dumperCallThread();
void setDumperCallThread(unsigned long t);
private: private:
enum CallLoadResult { CallLoadOk, CallLoadError, CallLoadNoQtApp, CallLoadAlreadyLoaded }; enum CallLoadResult { CallLoadOk, CallLoadError, CallLoadNoQtApp, CallLoadAlreadyLoaded };
...@@ -103,6 +117,9 @@ private: ...@@ -103,6 +117,9 @@ private:
bool initResolveSymbols(QString *errorMessage); bool initResolveSymbols(QString *errorMessage);
bool initKnownTypes(QString *errorMessage); bool initKnownTypes(QString *errorMessage);
inline DumpResult dumpTypeI(const WatchData &d, bool dumpChildren,
QList<WatchData> *result, QString *errorMessage);
bool getTypeSize(const QString &typeName, int *size, QString *errorMessage); bool getTypeSize(const QString &typeName, int *size, QString *errorMessage);
bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage); bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage);
bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr, bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr,
...@@ -134,6 +151,8 @@ private: ...@@ -134,6 +151,8 @@ private:
QStringList m_failedTypes; QStringList m_failedTypes;
QtDumperHelper m_helper; QtDumperHelper m_helper;
unsigned long m_dumperCallThread;
QString m_goCommand;
}; };
} // namespace Internal } // namespace Internal
......
...@@ -41,7 +41,11 @@ enum { debugExc = 0 }; ...@@ -41,7 +41,11 @@ enum { debugExc = 0 };
// Special exception codes. // Special exception codes.
enum { cppExceptionCode = 0xe06d7363, startupCompleteTrap = 0x406d1388, enum { cppExceptionCode = 0xe06d7363, startupCompleteTrap = 0x406d1388,
rpcServerUnavailableExceptionCode = 0x6ba, rpcServerUnavailableExceptionCode = 0x6ba,
dllNotFoundExceptionCode = 0xc0000135 }; dllNotFoundExceptionCode = 0xc0000135,
dllInitFailed = 0xc0000142,
missingSystemFile = 0xc0000143,
appInitFailed = 0xc0000143
};
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
...@@ -172,6 +176,12 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str) ...@@ -172,6 +176,12 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str)
case dllNotFoundExceptionCode: case dllNotFoundExceptionCode:
str << "DLL not found"; str << "DLL not found";
break; break;
case dllInitFailed:
str << "DLL failed to initialize";
break;
case missingSystemFile:
str << "System file is missing";
break;
case EXCEPTION_ACCESS_VIOLATION: { case EXCEPTION_ACCESS_VIOLATION: {
const bool writeOperation = e->ExceptionInformation[0]; const bool writeOperation = e->ExceptionInformation[0];
str << (writeOperation ? "write" : "read") str << (writeOperation ? "write" : "read")
......
...@@ -40,6 +40,10 @@ ...@@ -40,6 +40,10 @@
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
const char *CdbStackTraceContext::winFuncFastSystemCallRet = "ntdll!KiFastSystemCallRet";
const char *CdbStackTraceContext::winFuncDebugBreakPoint = "ntdll!DbgBreakPoint";
const char *CdbStackTraceContext::winFuncWaitForPrefix = "kernel32!WaitFor";
CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) : CdbStackTraceContext::CdbStackTraceContext(const QSharedPointer<CdbDumperHelper> &dumper) :
m_dumper(dumper), m_dumper(dumper),
m_cif(dumper->comInterfaces()), m_cif(dumper->comInterfaces()),
...@@ -232,6 +236,7 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif, ...@@ -232,6 +236,7 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
ThreadData *t, ThreadData *t,
QString *errorMessage) QString *errorMessage)
{ {
enum { MaxFrames = 2 };
ULONG currentThread; ULONG currentThread;
HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(&currentThread); HRESULT hr = cif.debugSystemObjects->GetCurrentThreadId(&currentThread);
if (FAILED(hr)) { if (FAILED(hr)) {
...@@ -246,29 +251,38 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif, ...@@ -246,29 +251,38 @@ static inline bool getStoppedThreadState(const CdbComInterfaces &cif,
} }
} }
ULONG frameCount; ULONG frameCount;
DEBUG_STACK_FRAME topFrame[1]; // Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
hr = cif.debugControl->GetStackTrace(0, 0, 0, topFrame, 1, &frameCount); // not interesting for display.
DEBUG_STACK_FRAME frames[MaxFrames];
hr = cif.debugControl->GetStackTrace(0, 0, 0, frames, MaxFrames, &frameCount);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetStackTrace", hr)); *errorMessage = msgGetThreadStateFailed(t->id, msgComFailed("GetStackTrace", hr));
return false; return false;
} }
// Ignore the top frame if it is "ntdll!KiFastSystemCallRet", which is
t->address = topFrame[0].InstructionOffset; // not interesting for display.
WCHAR wszBuf[MAX_PATH]; WCHAR wszBuf[MAX_PATH];
for (int frame = 0; frame < MaxFrames; frame++) {
cif.debugSymbols->GetNameByOffsetWide(topFrame[0].InstructionOffset, wszBuf, MAX_PATH, 0, 0); cif.debugSymbols->GetNameByOffsetWide(frames[frame].InstructionOffset, wszBuf, MAX_PATH, 0, 0);
t->function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)); t->function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
ULONG ulLine; if (frame != 0 || t->function != QLatin1String(CdbStackTraceContext::winFuncFastSystemCallRet)) {
hr = cif.debugSymbols->GetLineByOffsetWide(topFrame[0].InstructionOffset, &ulLine, wszBuf, MAX_PATH, 0, 0); t->address = frames[frame].InstructionOffset;
if (SUCCEEDED(hr)) { cif.debugSymbols->GetNameByOffsetWide(frames[frame].InstructionOffset, wszBuf, MAX_PATH, 0, 0);
t->line = ulLine; t->function = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
// Just display base name ULONG ulLine;
t->file = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf)); hr = cif.debugSymbols->GetLineByOffsetWide(frames[frame].InstructionOffset, &ulLine, wszBuf, MAX_PATH, 0, 0);
if (!t->file.isEmpty()) { if (SUCCEEDED(hr)) {
const int slashPos = t->file.lastIndexOf(QLatin1Char('\\')); t->line = ulLine;
if (slashPos != -1) // Just display base name
t->file.remove(0, slashPos + 1); t->file = QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf));
} if (!t->file.isEmpty()) {
const int slashPos = t->file.lastIndexOf(QLatin1Char('\\'));
if (slashPos != -1)
t->file.remove(0, slashPos + 1);
}
}
break;
} // was not "ntdll!KiFastSystemCallRet"
} }
return true; return true;
}