forked from qt-creator/qt-creator
CDB: Case-normalize file names returned by CDB
Fixing various problems with breakpoints and opened files caused by CDB returning lower-case file names. Task-number: QTCREATORBUG-438 Reviewed-by: hjk <qtc-committer@nokia.com> Acked-by: mariusSO <qt-info@nokia.com>
This commit is contained in:
@@ -62,6 +62,7 @@ SOURCES += \
|
|||||||
|
|
||||||
FORMS += $$PWD/cdboptionspagewidget.ui
|
FORMS += $$PWD/cdboptionspagewidget.ui
|
||||||
|
|
||||||
|
LIBS+=-lpsapi
|
||||||
} else {
|
} else {
|
||||||
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
||||||
} # exists($$CDB_PATH)
|
} # exists($$CDB_PATH)
|
||||||
|
@@ -37,6 +37,8 @@
|
|||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QMap>
|
#include <QtCore/QMap>
|
||||||
|
|
||||||
|
#include <psapi.h>
|
||||||
|
|
||||||
enum { debugBP = 0 };
|
enum { debugBP = 0 };
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
@@ -215,16 +217,106 @@ bool CDBBreakPoint::add(CIDebugControl* debugControl,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure file can be found in editor manager and text markers
|
// Helper for normalizing file names:
|
||||||
// Use '/' and capitalize drive letter
|
// Map the device paths in a file name to back to drive letters
|
||||||
QString CDBBreakPoint::canonicalSourceFile(const QString &f)
|
// "/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;
|
return f;
|
||||||
QString rc = QDir::fromNativeSeparators(f);
|
// Get the file size. We need a non-empty file to map it.
|
||||||
if (rc.size() > 2 && rc.at(1) == QLatin1Char(':'))
|
DWORD dwFileSizeHi = 0;
|
||||||
rc[0] = rc.at(0).toUpper();
|
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);
|
||||||
return rc;
|
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)
|
bool CDBBreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
|
||||||
@@ -267,7 +359,7 @@ bool CDBBreakPoint::parseExpression(const QString &expr)
|
|||||||
conditionPos = expr.indexOf(sourceFileQuote, colonPos + 1);
|
conditionPos = expr.indexOf(sourceFileQuote, colonPos + 1);
|
||||||
if (conditionPos == -1)
|
if (conditionPos == -1)
|
||||||
return false;
|
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);
|
const QString lineNumberS = expr.mid(colonPos + 1, conditionPos - colonPos - 1);
|
||||||
bool lineNumberOk = false;
|
bool lineNumberOk = false;
|
||||||
lineNumber = lineNumberS.toInt(&lineNumberOk);
|
lineNumber = lineNumberS.toInt(&lineNumberOk);
|
||||||
|
@@ -80,7 +80,8 @@ struct CDBBreakPoint
|
|||||||
QString *errorMessage, QStringList *warnings);
|
QString *errorMessage, QStringList *warnings);
|
||||||
|
|
||||||
// Return a 'canonical' file (using '/' and capitalized drive letter)
|
// 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 fileName; // short name of source file
|
||||||
QString condition; // condition associated with breakpoint
|
QString condition; // condition associated with breakpoint
|
||||||
|
@@ -625,12 +625,14 @@ void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters>
|
|||||||
{
|
{
|
||||||
if (debugCDBExecution)
|
if (debugCDBExecution)
|
||||||
qDebug() << "startDebugger" << *sp;
|
qDebug() << "startDebugger" << *sp;
|
||||||
|
CDBBreakPoint::clearNormalizeFileNameCache();
|
||||||
setState(AdapterStarting, Q_FUNC_INFO, __LINE__);
|
setState(AdapterStarting, Q_FUNC_INFO, __LINE__);
|
||||||
m_d->checkVersion();
|
m_d->checkVersion();
|
||||||
if (m_d->m_hDebuggeeProcess) {
|
if (m_d->m_hDebuggeeProcess) {
|
||||||
warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged."));
|
warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged."));
|
||||||
setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__);
|
setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__);
|
||||||
emit startFailed();
|
emit startFailed();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
m_d->clearDisplay();
|
m_d->clearDisplay();
|
||||||
m_d->m_inferiorStartupComplete = false;
|
m_d->m_inferiorStartupComplete = false;
|
||||||
|
@@ -116,7 +116,7 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
|
|||||||
frame.line = ulLine;
|
frame.line = ulLine;
|
||||||
// Vitally important to use canonical file that matches editormanager,
|
// Vitally important to use canonical file that matches editormanager,
|
||||||
// else the marker will not show.
|
// 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);
|
m_frames.push_back(frame);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user