forked from qt-creator/qt-creator
Debugger[New CDB]: Improve 32bit debugging on 64bit systems.
- Ignore WOW64 breakpoints. Restructure code to examine stop reason before notifications to be able handle special stop reasons in a simpler way. - Fix autodetection to look into %ProgramFiles% (x64) as well.
This commit is contained in:
@@ -1361,19 +1361,85 @@ static const char *cdbStatusName(unsigned long s)
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void CdbEngine::handleSessionIdle(const QByteArray &message)
|
||||
/* Examine how to react to a stop. */
|
||||
enum StopActionFlags
|
||||
{
|
||||
// Report options
|
||||
StopReportLog = 0x1,
|
||||
StopReportStatusMessage = 0x2,
|
||||
StopReportParseError = 0x2,
|
||||
StopShowExceptionMessageBox = 0x4,
|
||||
// Notify stop or just continue
|
||||
StopNotifyStop = 0x8,
|
||||
StopIgnoreContinue = 0x10
|
||||
};
|
||||
|
||||
unsigned CdbEngine::examineStopReason(const QByteArray &messageIn,
|
||||
QString *message,
|
||||
QString *exceptionBoxMessage) const
|
||||
{
|
||||
// Report stop reason (GDBMI)
|
||||
GdbMi stopReason;
|
||||
stopReason.fromString(messageIn);
|
||||
if (debug)
|
||||
qDebug("%s", stopReason.toString(true, 4).constData());
|
||||
const QByteArray reason = stopReason.findChild("reason").data();
|
||||
if (reason.isEmpty()) {
|
||||
*message = tr("Malformed stop response received.");
|
||||
return StopReportParseError|StopNotifyStop;
|
||||
}
|
||||
const int threadId = stopReason.findChild("threadId").data().toInt();
|
||||
if (reason == "breakpoint") {
|
||||
const int number = stopReason.findChild("breakpointId").data().toInt();
|
||||
const BreakpointId id = breakHandler()->findBreakpointByNumber(number);
|
||||
if (id && breakHandler()->type(id) == Watchpoint) {
|
||||
*message = msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId));
|
||||
} else {
|
||||
*message = msgBreakpointTriggered(id, number, QString::number(threadId));
|
||||
}
|
||||
return StopReportStatusMessage|StopNotifyStop;
|
||||
}
|
||||
if (reason == "exception") {
|
||||
WinException exception;
|
||||
exception.fromGdbMI(stopReason);
|
||||
#ifdef Q_OS_WIN
|
||||
// It is possible to hit on a startup trap while stepping (if something
|
||||
// pulls DLLs. Avoid showing a 'stopped' Message box.
|
||||
if (exception.exceptionCode == winExceptionStartupCompleteTrap)
|
||||
return StopNotifyStop;
|
||||
const QString description = exception.toString();
|
||||
// WOW 64 breakpoint: just report in log and continue
|
||||
if (exception.exceptionCode == winExceptionWX86Breakpoint) {
|
||||
*message = description;
|
||||
return StopIgnoreContinue|StopReportLog;
|
||||
}
|
||||
if (isDebuggerWinException(exception.exceptionCode)) {
|
||||
*message = msgInterrupted();
|
||||
return StopReportStatusMessage|StopNotifyStop;
|
||||
}
|
||||
#endif
|
||||
*exceptionBoxMessage = msgStoppedByException(description, QString::number(threadId));
|
||||
*message = description;
|
||||
return StopShowExceptionMessageBox|StopReportStatusMessage|StopNotifyStop;
|
||||
}
|
||||
*message = msgStopped(QLatin1String(reason));
|
||||
return StopReportStatusMessage|StopNotifyStop;
|
||||
}
|
||||
|
||||
void CdbEngine::handleSessionIdle(const QByteArray &messageBA)
|
||||
{
|
||||
if (!m_hasDebuggee)
|
||||
return;
|
||||
|
||||
if (debug)
|
||||
qDebug("CdbEngine::handleSessionIdle %dms '%s' in state '%s', special mode %d",
|
||||
elapsedLogTime(), message.constData(),
|
||||
elapsedLogTime(), messageBA.constData(),
|
||||
stateName(state()), m_specialStopMode);
|
||||
|
||||
// Switch source level debugging
|
||||
syncOperateByInstruction(m_operateByInstructionPending);
|
||||
|
||||
// Engine-special stop reasons: Breakpoints and setup
|
||||
const SpecialStopMode specialStopMode = m_specialStopMode;
|
||||
m_specialStopMode = NoSpecialStop;
|
||||
|
||||
@@ -1387,23 +1453,32 @@ void CdbEngine::handleSessionIdle(const QByteArray &message)
|
||||
case NoSpecialStop:
|
||||
break;
|
||||
}
|
||||
switch(state()) { // Temporary stop at beginning
|
||||
case EngineSetupRequested:
|
||||
|
||||
if (state() == EngineSetupRequested) { // Temporary stop at beginning
|
||||
if (debug)
|
||||
qDebug("notifyEngineSetupOk");
|
||||
notifyEngineSetupOk();
|
||||
return;
|
||||
case InferiorSetupRequested:
|
||||
return;
|
||||
case InferiorStopRequested:
|
||||
case InferiorRunOk:
|
||||
break; // Proper stop of inferior handled below.
|
||||
}
|
||||
|
||||
default:
|
||||
qWarning("WARNING: CdbEngine::handleSessionAccessible called in state %s", stateName(state()));
|
||||
// Further examine stop and report to user
|
||||
QString message;
|
||||
QString exceptionBoxMessage;
|
||||
const unsigned stopFlags = examineStopReason(messageBA, &message, &exceptionBoxMessage);
|
||||
// Do the non-blocking log reporting
|
||||
if (stopFlags & StopReportLog)
|
||||
showMessage(message, LogMisc);
|
||||
if (stopFlags & StopReportStatusMessage)
|
||||
showStatusMessage(message);
|
||||
if (stopFlags & StopReportParseError)
|
||||
showMessage(message, LogError);
|
||||
// Ignore things like WOW64
|
||||
if (stopFlags & StopIgnoreContinue) {
|
||||
postCommand("g", 0);
|
||||
return;
|
||||
}
|
||||
// Handle stop.
|
||||
// Notify about state and send off command sequence to get stack, etc.
|
||||
if (stopFlags & StopNotifyStop) {
|
||||
if (state() == InferiorStopRequested) {
|
||||
if (debug)
|
||||
qDebug("notifyInferiorStopOk");
|
||||
@@ -1413,53 +1488,18 @@ void CdbEngine::handleSessionIdle(const QByteArray &message)
|
||||
qDebug("notifyInferiorSpontaneousStop");
|
||||
notifyInferiorSpontaneousStop();
|
||||
}
|
||||
// Start sequence to get all relevant data. Hack: Avoid module reload?
|
||||
// Start sequence to get all relevant data.
|
||||
unsigned sequence = CommandListStack|CommandListThreads;
|
||||
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_REGISTER)))
|
||||
sequence |= CommandListRegisters;
|
||||
if (debuggerCore()->isDockVisible(QLatin1String(Constants::DOCKWIDGET_MODULES)))
|
||||
sequence |= CommandListModules;
|
||||
postCommandSequence(sequence);
|
||||
// Report stop reason (GDBMI)
|
||||
GdbMi stopReason;
|
||||
stopReason.fromString(message);
|
||||
if (debug)
|
||||
qDebug("%s", stopReason.toString(true, 4).constData());
|
||||
const QByteArray reason = stopReason.findChild("reason").data();
|
||||
if (reason.isEmpty()) {
|
||||
showStatusMessage(tr("Malformed stop response received."), LogError);
|
||||
return;
|
||||
}
|
||||
const int threadId = stopReason.findChild("threadId").data().toInt();
|
||||
if (reason == "breakpoint") {
|
||||
const int number = stopReason.findChild("breakpointId").data().toInt();
|
||||
const BreakpointId id = breakHandler()->findBreakpointByNumber(number);
|
||||
if (id && breakHandler()->type(id) == Watchpoint) {
|
||||
showStatusMessage(msgWatchpointTriggered(id, number, breakHandler()->address(id), QString::number(threadId)));
|
||||
} else {
|
||||
showStatusMessage(msgBreakpointTriggered(id, number, QString::number(threadId)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (reason == "exception") {
|
||||
WinException exception;
|
||||
exception.fromGdbMI(stopReason);
|
||||
#ifdef Q_OS_WIN
|
||||
// It is possible to hit on a startup trap while stepping (if something
|
||||
// pulls DLLs. Avoid showing a 'stopped' Message box.
|
||||
if (exception.exceptionCode == winExceptionStartupCompleteTrap)
|
||||
return;
|
||||
if (isDebuggerWinException(exception.exceptionCode)) {
|
||||
showStatusMessage(msgInterrupted());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
const QString description = exception.toString();
|
||||
showStatusMessage(msgStoppedByException(description, QString::number(threadId)));
|
||||
showStoppedByExceptionMessageBox(description);
|
||||
return;
|
||||
}
|
||||
showStatusMessage(msgStopped(QLatin1String(reason)));
|
||||
// After the sequence has been sent off and CDB is pondering the commands,
|
||||
// pop up a message box for exceptions.
|
||||
if (stopFlags & StopShowExceptionMessageBox)
|
||||
showStoppedByExceptionMessageBox(exceptionBoxMessage);
|
||||
}
|
||||
|
||||
void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
|
||||
|
||||
@@ -152,6 +152,8 @@ private slots:
|
||||
private:
|
||||
enum SpecialStopMode { NoSpecialStop, SpecialStopSynchronizeBreakpoints };
|
||||
|
||||
unsigned examineStopReason(const QByteArray &messageIn, QString *message,
|
||||
QString *exceptionBoxMessage) const;
|
||||
bool commandsPending() const;
|
||||
void handleExtensionMessage(char t, int token, const QByteArray &what, const QByteArray &message);
|
||||
bool doSetupEngine(QString *errorMessage);
|
||||
|
||||
@@ -174,6 +174,21 @@ bool CdbOptions::equals(const CdbOptions &rhs) const
|
||||
&& breakEvents == rhs.breakEvents;
|
||||
}
|
||||
|
||||
// Check the CDB executable and accumulate the list of checked paths
|
||||
// for reporting.
|
||||
static QString checkCdbExecutable(const QString &programDir, const QString &postfix,
|
||||
QStringList *checkedDirectories = 0)
|
||||
{
|
||||
QString executable = programDir;
|
||||
executable += QLatin1String("/Debugging Tools For Windows");
|
||||
executable += postfix;
|
||||
if (checkedDirectories)
|
||||
checkedDirectories->push_back(QDir::toNativeSeparators(executable));
|
||||
executable += QLatin1String("/cdb.exe");
|
||||
const QFileInfo fi(executable);
|
||||
return fi.isFile() && fi.isExecutable() ? fi.absoluteFilePath() : QString();
|
||||
}
|
||||
|
||||
bool CdbOptions::autoDetectExecutable(QString *outPath, bool *is64bitIn /* = 0 */,
|
||||
QStringList *checkedDirectories /* = 0 */)
|
||||
{
|
||||
@@ -182,48 +197,54 @@ bool CdbOptions::autoDetectExecutable(QString *outPath, bool *is64bitIn /* = 0
|
||||
static const char *postFixes[] = {" (x64)", " 64-bit", " (x86)", " (x32)" };
|
||||
enum { first32bitIndex = 2 };
|
||||
|
||||
outPath->clear();
|
||||
if (checkedDirectories)
|
||||
checkedDirectories->clear();
|
||||
|
||||
outPath->clear();
|
||||
const QByteArray programDirB = qgetenv("ProgramFiles");
|
||||
if (programDirB.isEmpty())
|
||||
const QString programDir = QString::fromLocal8Bit(qgetenv("ProgramFiles"));
|
||||
if (programDir.isEmpty())
|
||||
return false;
|
||||
|
||||
const QString programDir = QString::fromLocal8Bit(programDirB) + QLatin1Char('/');
|
||||
const QString installDir = QLatin1String("Debugging Tools For Windows");
|
||||
const QString executable = QLatin1String("/cdb.exe");
|
||||
|
||||
QString path = programDir + installDir;
|
||||
if (checkedDirectories)
|
||||
checkedDirectories->push_back(path);
|
||||
const QFileInfo fi(path + executable);
|
||||
// Plain system installation
|
||||
if (fi.isFile() && fi.isExecutable()) {
|
||||
*outPath = fi.absoluteFilePath();
|
||||
if (is64bitIn)
|
||||
#ifdef Q_OS_WIN
|
||||
*is64bitIn = Utils::winIs64BitSystem();
|
||||
const bool systemIs64Bit = Utils::winIs64BitSystem();
|
||||
#else
|
||||
*is64bitIn = false;
|
||||
const bool systemIs64Bit = false;
|
||||
#endif
|
||||
// Plain system installation. 32/64 Bit matches the system.
|
||||
*outPath = checkCdbExecutable(programDir, QString(), checkedDirectories);
|
||||
if (!outPath->isEmpty()) {
|
||||
if (is64bitIn)
|
||||
*is64bitIn = systemIs64Bit;
|
||||
return true;
|
||||
}
|
||||
// Try the post fixes
|
||||
const int rootLength = path.size();
|
||||
for (unsigned i = 0; i < sizeof(postFixes)/sizeof(const char*); i++) {
|
||||
path.truncate(rootLength);
|
||||
path += QLatin1String(postFixes[i]);
|
||||
if (checkedDirectories)
|
||||
checkedDirectories->push_back(path);
|
||||
const QFileInfo fi2(path + executable);
|
||||
if (fi2.isFile() && fi2.isExecutable()) {
|
||||
*outPath = checkCdbExecutable(programDir, QLatin1String(postFixes[i]), checkedDirectories);
|
||||
if (!outPath->isEmpty()) {
|
||||
if (is64bitIn)
|
||||
*is64bitIn = i < first32bitIndex;
|
||||
*outPath = fi2.absoluteFilePath();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// A 32bit-compile running on a 64bit system sees the 64 bit installation
|
||||
// as "$ProgramFiles (x64)/Debugging Tools..." and (untested), a 64 bit-
|
||||
// compile running on a 64bit system sees the 32 bit installation as
|
||||
// "$ProgramFiles (x86)/Debugging Tools..." (assuming this works at all)
|
||||
#ifdef Q_OS_WIN64
|
||||
*outPath = checkCdbExecutable(programDir + QLatin1String(" (x32)"), QString(), checkedDirectories);
|
||||
if (!outPath->isEmpty()) {
|
||||
if (is64bitIn)
|
||||
*is64bitIn = false;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
*outPath = checkCdbExecutable(programDir + QLatin1String(" (x64)"), QString(), checkedDirectories);
|
||||
if (!outPath->isEmpty()) {
|
||||
if (is64bitIn)
|
||||
*is64bitIn = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -292,6 +292,9 @@ void formatWindowsException(unsigned long code, quint64 address,
|
||||
case winExceptionRpcServerInvalid:
|
||||
str << "Invalid RPC server";
|
||||
break;
|
||||
case winExceptionWX86Breakpoint:
|
||||
str << "Win32 x86 emulation subsystem breakpoint hit";
|
||||
break;
|
||||
case EXCEPTION_ACCESS_VIOLATION: {
|
||||
const bool writeOperation = info1;
|
||||
str << (writeOperation ? "write" : "read")
|
||||
|
||||
@@ -75,7 +75,8 @@ enum { winExceptionCppException = 0xe06d7363,
|
||||
winExceptionDllEntryPointNoFound = 0xc0000139,
|
||||
winExceptionDllInitFailed = 0xc0000142,
|
||||
winExceptionMissingSystemFile = 0xc0000143,
|
||||
winExceptionAppInitFailed = 0xc0000143
|
||||
winExceptionAppInitFailed = 0xc0000143,
|
||||
winExceptionWX86Breakpoint = 0x4000001f
|
||||
};
|
||||
|
||||
// Format windows Exception
|
||||
|
||||
Reference in New Issue
Block a user