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 9f74ec7f9ef1f91f9f6df87f8632db11e0ab8f0d..ae17ff1ea0f3842c3534fa4b601c1eaa320ff704 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; 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); }