diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index e0b397fcef5a724088fffbb011843414d94e80be..b207360bd728fbdf61491be3450673f6c0bdd4fa 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -49,6 +49,18 @@ using namespace Debugger::Internal; // ////////////////////////////////////////////////////////////////// +// Compare file names case insensitively on Windows. +static inline bool fileNameMatch(const QString &f1, const QString &f2) +{ + return f1.compare(f2, +#ifdef Q_OS_WIN + Qt::CaseInsensitive +#else + Qt::CaseSensitive +#endif + ) == 0; +} + namespace Debugger { namespace Internal { @@ -236,7 +248,7 @@ bool BreakpointData::isLocatedAt(const QString &fileName_, int lineNumber_) cons return true; return false; */ - return lineNumber_ == markerLineNumber && fileName_ == markerFileName; + return lineNumber_ == markerLineNumber && fileNameMatch(fileName_, markerFileName); } bool BreakpointData::conditionsMatch() const @@ -310,7 +322,7 @@ int BreakHandler::findBreakpoint(const BreakpointData &needle) return index; // at least at a position we were looking for // FIXME: breaks multiple breakpoints at the same location - if (data->fileName == needle.bpFileName + if (fileNameMatch(data->fileName, needle.bpFileName) && data->lineNumber == needle.bpLineNumber) return index; } diff --git a/src/plugins/debugger/cdb/cdb.pri b/src/plugins/debugger/cdb/cdb.pri index a1ab1d4c4e6c9198d1834a424166c94153bb90f1..6ac5997efa9c610c48920b59481b94bfe55bb5f6 100644 --- a/src/plugins/debugger/cdb/cdb.pri +++ b/src/plugins/debugger/cdb/cdb.pri @@ -62,6 +62,7 @@ SOURCES += \ FORMS += $$PWD/cdboptionspagewidget.ui +LIBS+=-lpsapi } else { message("Debugging Tools for Windows could not be found in $$CDB_PATH") } # exists($$CDB_PATH) diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.cpp b/src/plugins/debugger/cdb/cdbbreakpoint.cpp index 40bec69fae03245705bd45dd02c2e8124da52521..4393c6a36122f5bb9b8232bc86120ed53f85b305 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.cpp +++ b/src/plugins/debugger/cdb/cdbbreakpoint.cpp @@ -37,6 +37,8 @@ #include <QtCore/QDebug> #include <QtCore/QMap> +#include <psapi.h> + enum { debugBP = 0 }; namespace Debugger { @@ -215,16 +217,106 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl, return true; } -// Make sure file can be found in editor manager and text markers -// Use '/' and capitalize drive letter -QString CDBBreakPoint::canonicalSourceFile(const QString &f) +// Helper for normalizing file names: +// Map the device paths in a file name to back to drive letters +// "/Device/HarddiskVolume1/file.cpp" -> "C:/file.cpp" + +static bool mapDeviceToDriveLetter(QString *s) { - if (f.isEmpty()) + enum { bufSize = 512 }; + // Retrieve drive letters and get their device names. + // Do not cache as it may change due to removable/network drives. + TCHAR driveLetters[bufSize]; + if (!GetLogicalDriveStrings(bufSize-1, driveLetters)) + return false; + + TCHAR driveName[MAX_PATH]; + TCHAR szDrive[3] = TEXT(" :"); + for (const TCHAR *driveLetter = driveLetters; *driveLetter; driveLetter++) { + szDrive[0] = *driveLetter; // Look up each device name + if (QueryDosDevice(szDrive, driveName, MAX_PATH)) { + const QString deviceName = QString::fromUtf16(driveName); + if (s->startsWith(deviceName)) { + s->replace(0, deviceName.size(), QString::fromUtf16(szDrive)); + return true; + } + } + } + return false; +} + +// Helper for normalizing file names: +// Determine normalized case of a Windows file name (camelcase.cpp -> CamelCase.cpp) +// as the debugger reports lower case file names. +// Restriction: File needs to exists and be non-empty and will be to be opened/mapped. +// This is the MSDN-recommended way of doing that. The result should be cached. + +static inline QString normalizeFileNameCaseHelper(const QString &f) +{ + HANDLE hFile = CreateFile(f.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hFile == INVALID_HANDLE_VALUE) return f; - QString rc = QDir::fromNativeSeparators(f); - if (rc.size() > 2 && rc.at(1) == QLatin1Char(':')) - rc[0] = rc.at(0).toUpper(); - return rc; + // Get the file size. We need a non-empty file to map it. + DWORD dwFileSizeHi = 0; + DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi); + if (dwFileSizeLo == 0 && dwFileSizeHi == 0) { + CloseHandle(hFile); + return f; + } + // Create a file mapping object. + HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 1, NULL); + if (!hFileMap) { + CloseHandle(hFile); + return f; + } + + // Create a file mapping to get the file name. + void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1); + if (!pMem) { + CloseHandle(hFileMap); + CloseHandle(hFile); + return f; + } + + QString rc; + WCHAR pszFilename[MAX_PATH]; + pszFilename[0] = 0; + // Get a file name of the form "/Device/HarddiskVolume1/file.cpp" + if (GetMappedFileName (GetCurrentProcess(), pMem, pszFilename, MAX_PATH)) { + rc = QString::fromUtf16(pszFilename); + if (!mapDeviceToDriveLetter(&rc)) + rc.clear(); + } + + UnmapViewOfFile(pMem); + CloseHandle(hFileMap); + CloseHandle(hFile); + return rc.isEmpty() ? f : rc; +} + +// Make sure file can be found in editor manager and text markers +// Use '/', correct case and capitalize drive letter. Use a cache. + +typedef QHash<QString, QString> NormalizedFileCache; +Q_GLOBAL_STATIC(NormalizedFileCache, normalizedFileNameCache) + +QString CDBBreakPoint::normalizeFileName(const QString &f) +{ + QTC_ASSERT(!f.isEmpty(), return f) + const NormalizedFileCache::const_iterator it = normalizedFileNameCache()->constFind(f); + if (it != normalizedFileNameCache()->constEnd()) + return it.value(); + QString normalizedName = QDir::fromNativeSeparators(normalizeFileNameCaseHelper(f)); + // Upcase drive letter for consistency even if case mapping fails. + if (normalizedName.size() > 2 && normalizedName.at(1) == QLatin1Char(':')) + normalizedName[0] = normalizedName.at(0).toUpper(); + normalizedFileNameCache()->insert(f, normalizedName); + return f; +} + +void CDBBreakPoint::clearNormalizeFileNameCache() +{ + normalizedFileNameCache()->clear(); } bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage) @@ -267,7 +359,7 @@ bool CDBBreakPoint::parseExpression(const QString &expr) conditionPos = expr.indexOf(sourceFileQuote, colonPos + 1); if (conditionPos == -1) return false; - fileName = canonicalSourceFile(expr.mid(1, colonPos - 1)); + fileName = normalizeFileName(expr.mid(1, colonPos - 1)); const QString lineNumberS = expr.mid(colonPos + 1, conditionPos - colonPos - 1); bool lineNumberOk = false; lineNumber = lineNumberS.toInt(&lineNumberOk); diff --git a/src/plugins/debugger/cdb/cdbbreakpoint.h b/src/plugins/debugger/cdb/cdbbreakpoint.h index 7b1379617e506e449003fe7dad0c75e6cab759db..3a1e8e09b5f7b787b8788f444fd204e46336f47d 100644 --- a/src/plugins/debugger/cdb/cdbbreakpoint.h +++ b/src/plugins/debugger/cdb/cdbbreakpoint.h @@ -80,7 +80,8 @@ struct CDBBreakPoint QString *errorMessage, QStringList *warnings); // Return a 'canonical' file (using '/' and capitalized drive letter) - static QString canonicalSourceFile(const QString &f); + static QString normalizeFileName(const QString &f); + static void clearNormalizeFileNameCache(); QString fileName; // short name of source file QString condition; // condition associated with breakpoint diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index 333a84dfc9405df3059bd5b39ad1748c4cf1092f..54041804045aeafb289c899998d49de049a9679d 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -625,12 +625,14 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> { if (debugCDBExecution) qDebug() << "startDebugger" << *sp; + CDBBreakPoint::clearNormalizeFileNameCache(); setState(AdapterStarting, Q_FUNC_INFO, __LINE__); m_d->checkVersion(); if (m_d->m_hDebuggeeProcess) { warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged.")); setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__); emit startFailed(); + return; } m_d->clearDisplay(); m_d->m_inferiorStartupComplete = false; @@ -655,7 +657,7 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> m_d->m_dumper->reset(dumperLibName, dumperEnabled); setState(InferiorStarting, Q_FUNC_INFO, __LINE__); - manager()->showStatusMessage("Starting Debugger", -1); + manager()->showStatusMessage("Starting Debugger", messageTimeOut); QString errorMessage; bool rc = false; @@ -692,7 +694,6 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> break; } if (rc) { - manager()->showStatusMessage(tr("Debugger running"), -1); if (needWatchTimer) startWatchTimer(); emit startSuccessful(); @@ -732,8 +733,6 @@ bool CdbDebugEngine::startAttachDebugger(qint64 pid, DebuggerStartMode sm, QStri bool CdbDebugEngine::startDebuggerWithExecutable(DebuggerStartMode sm, QString *errorMessage) { - showStatusMessage("Starting Debugger", -1); - DEBUG_CREATE_PROCESS_OPTIONS dbgopts; memset(&dbgopts, 0, sizeof(dbgopts)); dbgopts.CreateFlags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; @@ -1196,7 +1195,7 @@ bool CdbDebugEnginePrivate::continueInferior(QString *errorMessage) setCodeLevel(); m_engine->killWatchTimer(); manager()->resetLocation(); - manager()->showStatusMessage(CdbDebugEngine::tr("Running requested..."), 5000); + manager()->showStatusMessage(CdbDebugEngine::tr("Running requested..."), messageTimeOut); if (!continueInferiorProcess(errorMessage)) break; diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 31df174b072b136f2322580ace3d2ead97872c3b..89a7fa15f74fbf3c64c60331da1baf54e7a19c8c 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -194,6 +194,8 @@ const char *executionStatusString(CIDebugControl *ctl); QString msgDebugEngineComResult(HRESULT hr); QString msgComFailed(const char *func, HRESULT hr); +enum { messageTimeOut = 5000 }; + enum { debugCDB = 0 }; enum { debugCDBExecution = 0 }; enum { debugCDBWatchHandling = 0 }; diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index 2269aab42cdcd906244ebb1db94727f52ef6cec0..2ebac2710fd0fe024148d01d4eee5a3069d6eba9 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -288,13 +288,13 @@ bool CdbDumperInitThread::ensureDumperInitialized(CdbDumperHelper &h, QString *e eventLoop.exec(QEventLoop::ExcludeUserInputEvents); QApplication::restoreOverrideCursor(); if (thread.m_ok) { - h.m_manager->showStatusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Stopped / Custom dumper library initialized."), -1); + h.m_manager->showStatusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Stopped / Custom dumper library initialized."), messageTimeOut); h.m_manager->showDebuggerOutput(LogMisc, h.m_helper.toString()); h.m_state = CdbDumperHelper::Initialized; } else { h.m_state = CdbDumperHelper::Disabled; // No message here *errorMessage = QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "The custom dumper library could not be initialized: %1").arg(*errorMessage); - h.m_manager->showStatusMessage(*errorMessage, -1); + h.m_manager->showStatusMessage(*errorMessage, messageTimeOut); h.m_manager->showQtDumperLibraryWarning(*errorMessage); } if (loadDebug) @@ -332,7 +332,7 @@ void CdbDumperInitThread ::run() break; } // Perform remaining initialization - emit statusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Initializing dumpers..."), -1); + emit statusMessage(QCoreApplication::translate("Debugger::Internal::CdbDumperHelper", "Initializing dumpers..."), 60000); m_ok = m_helper.initResolveSymbols(m_errorMessage) && m_helper.initKnownTypes(m_errorMessage); } @@ -404,7 +404,7 @@ void CdbDumperHelper::moduleLoadHook(const QString &module, HANDLE debuggeeHandl // for the thread to finish as this would lock up. if (m_tryInjectLoad && module.contains(QLatin1String("Qt"), Qt::CaseInsensitive)) { // Also shows up in the log window. - m_manager->showStatusMessage(msgLoading(m_library, true), 10000); + m_manager->showStatusMessage(msgLoading(m_library, true), messageTimeOut); QString errorMessage; SharedLibraryInjector sh(GetProcessId(debuggeeHandle)); if (sh.remoteInject(m_library, false, &errorMessage)) { diff --git a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp index 90329dfe04fbfbc34bf5b41d0fbe5b672353851b..e0c4ba0c798f4b50d06bd239af388a158cafd202 100644 --- a/src/plugins/debugger/cdb/cdbstacktracecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstacktracecontext.cpp @@ -116,7 +116,7 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa frame.line = ulLine; // Vitally important to use canonical file that matches editormanager, // else the marker will not show. - frame.file = CDBBreakPoint::canonicalSourceFile(QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf))); + frame.file = CDBBreakPoint::normalizeFileName(QString::fromUtf16(reinterpret_cast<const ushort *>(wszBuf))); } m_frames.push_back(frame); } diff --git a/src/plugins/projectexplorer/outputwindow.cpp b/src/plugins/projectexplorer/outputwindow.cpp index 69567498c57eac4dad40408a0d0d6f4e1dcc647d..9c0dc008e9aec71decc5954f13040b930621be46 100644 --- a/src/plugins/projectexplorer/outputwindow.cpp +++ b/src/plugins/projectexplorer/outputwindow.cpp @@ -88,7 +88,6 @@ OutputPane::OutputPane() m_stopAction->setEnabled(false); Core::Command *cmd = am->registerAction(m_stopAction, Constants::STOP, globalcontext); - cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+R"))); m_stopButton = new QToolButton; m_stopButton->setDefaultAction(cmd->action());