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
|
||||
|
||||
LIBS+=-lpsapi
|
||||
} else {
|
||||
message("Debugging Tools for Windows could not be found in $$CDB_PATH")
|
||||
} # exists($$CDB_PATH)
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user