Commit a60b3f67 authored by Friedemann Kleint's avatar Friedemann Kleint

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.
parent 18f9031b
......@@ -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,79 +1453,53 @@ 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()));
return;
}
// Handle stop.
if (state() == InferiorStopRequested) {
if (debug)
qDebug("notifyInferiorStopOk");
notifyInferiorStopOk();
} else {
if (debug)
qDebug("notifyInferiorSpontaneousStop");
notifyInferiorSpontaneousStop();
}
// Start sequence to get all relevant data. Hack: Avoid module reload?
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);
// 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;
}
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)));
// Notify about state and send off command sequence to get stack, etc.
if (stopFlags & StopNotifyStop) {
if (state() == InferiorStopRequested) {
if (debug)
qDebug("notifyInferiorStopOk");
notifyInferiorStopOk();
} 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;
if (debug)
qDebug("notifyInferiorSpontaneousStop");
notifyInferiorSpontaneousStop();
}
#endif
const QString description = exception.toString();
showStatusMessage(msgStoppedByException(description, QString::number(threadId)));
showStoppedByExceptionMessageBox(description);
return;
}
showStatusMessage(msgStopped(QLatin1String(reason)));
// 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);
}
// 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
......
Markdown is supported
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