Debugger: Rework detection of SDK-specified "auto" debuggers

Change-Id: I173752a41da7b34d64cb7e3e423992be464fc73b
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Tobias Hunger <tobias.hunger@digia.com>
This commit is contained in:
hjk
2013-10-17 12:48:16 +02:00
parent ed9752bc0a
commit 16cf6177d8
3 changed files with 181 additions and 176 deletions

View File

@@ -86,20 +86,104 @@ DebuggerKitInformation::DebuggerKitInformation()
QVariant DebuggerKitInformation::defaultValue(Kit *k) const QVariant DebuggerKitInformation::defaultValue(Kit *k) const
{ {
// This is only called from Kit::Kit()
// if (isValidDebugger(k)) {
// DebuggerItem *item = DebuggerItemManager::debuggerFromKit(k);
// QTC_ASSERT(item, return QVariant());
// return item->id;
// }
ToolChain *tc = ToolChainKitInformation::toolChain(k); ToolChain *tc = ToolChainKitInformation::toolChain(k);
return DebuggerItemManager::defaultDebugger(tc); QTC_ASSERT(tc, return QVariant());
const Abi toolChainAbi = tc->targetAbi();
foreach (const DebuggerItem &item, DebuggerItemManager::debuggers())
foreach (const Abi targetAbi, item.abis())
if (targetAbi.isCompatibleWith(toolChainAbi))
return item.id();
return QVariant();
} }
void DebuggerKitInformation::setup(Kit *k) void DebuggerKitInformation::setup(Kit *k)
{ {
k->setValue(DebuggerKitInformation::id(), defaultValue(k)); // Get one of the available debugger matching the kit's toolchain.
const ToolChain *tc = ToolChainKitInformation::toolChain(k);
const Abi toolChainAbi = tc ? tc->targetAbi() : Abi::hostAbi();
// This can be anything (Id, binary path, "auto")
const QVariant rawId = k->value(DebuggerKitInformation::id());
enum {
NotDetected, DetectedAutomatically, DetectedByFile, DetectedById
} detection = NotDetected;
DebuggerEngineType autoEngine = NoEngineType;
FileName fileName;
// With 3.0 we have:
// <value type="QString" key="Debugger.Information">{75ecf347-f221-44c3-b613-ea1d29929cd4}</value>
// Before we had:
// <valuemap type="QVariantMap" key="Debugger.Information">
// <value type="QString" key="Binary">/data/dev/debugger/gdb-git/gdb/gdb</value>
// <value type="int" key="EngineType">1</value>
// </valuemap>
// Or for force auto-detected CDB
// <valuemap type="QVariantMap" key="Debugger.Information">
// <value type="QString" key="Binary">auto</value>
// <value type="int" key="EngineType">4</value>
// </valuemap>
if (rawId.type() == QVariant::String) {
detection = DetectedById;
} else {
QMap<QString, QVariant> map = rawId.toMap();
QString binary = map.value(QLatin1String("Binary")).toString();
if (binary == QLatin1String("auto")) {
detection = DetectedAutomatically;
autoEngine = DebuggerEngineType(map.value(QLatin1String("EngineType")).toInt());
} else {
detection = DetectedByFile;
fileName = FileName::fromUserInput(binary);
}
}
QTC_CHECK(detection != NotDetected);
const DebuggerItem *bestItem = 0;
DebuggerItem::MatchLevel bestLevel = DebuggerItem::DoesNotMatch;
foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) {
const DebuggerItem *goodItem = 0;
if (detection == DetectedById && item.id() == rawId)
goodItem = &item;
if (detection == DetectedByFile && item.command() == fileName)
goodItem = &item;
if (detection == DetectedAutomatically && item.engineType() == autoEngine)
goodItem = &item;
if (goodItem) {
DebuggerItem::MatchLevel level = goodItem->matchTarget(toolChainAbi);
if (level > bestLevel) {
bestLevel = level;
bestItem = goodItem;
}
}
}
// If we have an existing debugger with matching id _and_
// matching target ABI we are fine.
if (bestItem) {
k->setValue(DebuggerKitInformation::id(), bestItem->id());
return;
}
// We didn't find an existing debugger that matched by whatever
// data we found in the kit (i.e. no id, filename, "auto")
// (or what we found did not match ABI-wise)
// Let's try to pick one with matching ABI.
QVariant bestId;
bestLevel = DebuggerItem::DoesNotMatch;
foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) {
DebuggerItem::MatchLevel level = item.matchTarget(toolChainAbi);
if (level > bestLevel) {
bestLevel = level;
bestId = item.id();
}
}
k->setValue(DebuggerKitInformation::id(), bestId);
} }
// Check the configuration errors and return a flag mask. Provide a quick check and // Check the configuration errors and return a flag mask. Provide a quick check and
@@ -145,57 +229,9 @@ static unsigned debuggerConfigurationErrors(const Kit *k)
const DebuggerItem *DebuggerKitInformation::debugger(const Kit *kit) const DebuggerItem *DebuggerKitInformation::debugger(const Kit *kit)
{ {
if (!kit) QTC_ASSERT(kit, return 0);
return 0;
const QVariant id = kit->value(DebuggerKitInformation::id()); const QVariant id = kit->value(DebuggerKitInformation::id());
return DebuggerItemManager::findById(id);
enum Detection { NotDetected, DetectedAutomatically, DetectedByFile, DetectedById };
Detection detection = NotDetected;
DebuggerEngineType autoEngine = NoEngineType;
FileName fileName;
// With 3.0 we have:
// <value type="QString" key="Debugger.Information">{75ecf347-f221-44c3-b613-ea1d29929cd4}</value>
// Before we had:
// <valuemap type="QVariantMap" key="Debugger.Information">
// <value type="QString" key="Binary">/data/dev/debugger/gdb-git/gdb/gdb</value>
// <value type="int" key="EngineType">1</value>
// </valuemap>
// Or for force auto-detected CDB
// <valuemap type="QVariantMap" key="Debugger.Information">
// <value type="QString" key="Binary">auto</value>
// <value type="int" key="EngineType">4</value>
// </valuemap>
if (id.type() == QVariant::String) {
detection = DetectedById;
} else {
QMap<QString, QVariant> map = id.toMap();
QString binary = map.value(QLatin1String("Binary")).toString();
if (binary == QLatin1String("auto")) {
detection = DetectedAutomatically;
autoEngine = DebuggerEngineType(map.value(QLatin1String("EngineType")).toInt());
} else {
detection = DetectedByFile;
fileName = FileName::fromUserInput(binary);
}
}
QTC_CHECK(detection != NotDetected);
foreach (const DebuggerItem &item, DebuggerItemManager::debuggers()) {
if (detection == DetectedById && item.id() == id)
return &item;
if (detection == DetectedByFile && item.command() == fileName)
return &item;
if (detection == DetectedAutomatically && item.engineType() == autoEngine)
return &item;
}
return 0;
} }
bool DebuggerKitInformation::isValidDebugger(const Kit *k) bool DebuggerKitInformation::isValidDebugger(const Kit *k)
@@ -297,32 +333,39 @@ static FileName userSettingsFileName()
return FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_FILENAME)); return FileName::fromString(settingsLocation.absolutePath() + QLatin1String(DEBUGGER_FILENAME));
} }
static QList<DebuggerItem> readDebuggers(const FileName &fileName) static void readDebuggers(const FileName &fileName, bool isSystem)
{ {
QList<DebuggerItem> result;
PersistentSettingsReader reader; PersistentSettingsReader reader;
if (!reader.load(fileName)) if (!reader.load(fileName))
return result; return;
QVariantMap data = reader.restoreValues(); QVariantMap data = reader.restoreValues();
// Check version // Check version
int version = data.value(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 0).toInt(); int version = data.value(QLatin1String(DEBUGGER_FILE_VERSION_KEY), 0).toInt();
if (version < 1) if (version < 1)
return result; return;
int count = data.value(QLatin1String(DEBUGGER_COUNT_KEY), 0).toInt(); int count = data.value(QLatin1String(DEBUGGER_COUNT_KEY), 0).toInt();
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
const QString key = QString::fromLatin1(DEBUGGER_DATA_KEY) + QString::number(i); const QString key = QString::fromLatin1(DEBUGGER_DATA_KEY) + QString::number(i);
if (!data.contains(key)) if (!data.contains(key))
break; continue;
const QVariantMap dbMap = data.value(key).toMap(); const QVariantMap dbMap = data.value(key).toMap();
DebuggerItem item; DebuggerItem item;
item.fromMap(dbMap); item.fromMap(dbMap);
result.append(item); if (isSystem) {
item.setAutoDetected(true);
// SDK debuggers are always considered to be up-to-date, so no need to recheck them.
} else {
// User settings.
if (item.isAutoDetected() && !item.isValid()) {
qWarning() << QString::fromLatin1("DebuggerItem \"%1\" (%2) dropped since it is not valid")
.arg(item.command().toString()).arg(item.id().toString());
continue;
}
}
DebuggerItemManager::registerDebugger(item);
} }
return result;
} }
QList<DebuggerItem> DebuggerItemManager::m_debuggers; QList<DebuggerItem> DebuggerItemManager::m_debuggers;
@@ -361,7 +404,7 @@ DebuggerItemModel *DebuggerItemManager::model()
return m_model; return m_model;
} }
void DebuggerItemManager::autoDetectCdbDebugger() void DebuggerItemManager::autoDetectCdbDebuggers()
{ {
QList<FileName> cdbs; QList<FileName> cdbs;
@@ -419,16 +462,34 @@ void DebuggerItemManager::autoDetectCdbDebugger()
} }
} }
void DebuggerItemManager::autoDetectDebuggers() void DebuggerItemManager::autoDetectGdbOrLldbDebuggers()
{ {
autoDetectCdbDebugger();
QStringList filters; QStringList filters;
filters.append(QLatin1String("gdb-i686-pc-mingw32")); filters.append(QLatin1String("gdb-i686-pc-mingw32"));
filters.append(QLatin1String("gdb")); filters.append(QLatin1String("gdb"));
filters.append(QLatin1String("lldb")); filters.append(QLatin1String("lldb"));
filters.append(QLatin1String("lldb-*")); filters.append(QLatin1String("lldb-*"));
// DebuggerItem result;
// result.setAutoDetected(true);
// result.setDisplayName(tr("Auto-detected for Tool Chain %1").arg(tc->displayName()));
/*
// Check suggestions from the SDK.
Environment env = Environment::systemEnvironment();
if (tc) {
tc->addToEnvironment(env); // Find MinGW gdb in toolchain environment.
QString path = tc->suggestedDebugger().toString();
if (!path.isEmpty()) {
const QFileInfo fi(path);
if (!fi.isAbsolute())
path = env.searchInPath(path);
result.command = FileName::fromString(path);
result.engineType = engineTypeFromBinary(path);
return maybeAddDebugger(result, false);
}
}
*/
QFileInfoList suspects; QFileInfoList suspects;
QStringList path = Environment::systemEnvironment().path(); QStringList path = Environment::systemEnvironment().path();
@@ -512,46 +573,16 @@ const DebuggerItem *DebuggerItemManager::findById(const QVariant &id)
void DebuggerItemManager::restoreDebuggers() void DebuggerItemManager::restoreDebuggers()
{ {
QList<DebuggerItem> dbsToCheck;
// Read debuggers from SDK // Read debuggers from SDK
QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName()); QFileInfo systemSettingsFile(Core::ICore::settings(QSettings::SystemScope)->fileName());
QList<DebuggerItem> dbsToRegister = readDebuggers(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(DEBUGGER_FILENAME)), true);
readDebuggers(FileName::fromString(systemSettingsFile.absolutePath() + QLatin1String(DEBUGGER_FILENAME)));
// These are autodetected.
for (int i = 0, n = dbsToRegister.size(); i != n; ++i)
dbsToRegister[i].setAutoDetected(true);
// SDK debuggers are always considered to be up-to-date, so no need to recheck them.
// Read all debuggers from user file. // Read all debuggers from user file.
foreach (const DebuggerItem &item, readDebuggers(userSettingsFileName())) { readDebuggers(userSettingsFileName(), false);
if (item.isAutoDetected())
dbsToCheck.append(item);
else
dbsToRegister.append(item);
}
// Keep debuggers that were not rediscovered but are still executable and delete the rest
foreach (const DebuggerItem &item, dbsToCheck) {
if (!item.isValid()) {
qWarning() << QString::fromLatin1("DebuggerItem \"%1\" (%2) dropped since it is not valid")
.arg(item.command().toString()).arg(item.id().toString());
} else {
dbsToRegister.append(item);
}
}
for (int i = 0, n = dbsToRegister.size(); i != n; ++i) {
DebuggerItem item = dbsToRegister.at(i);
if (findByCommand(item.command()))
continue;
addDebugger(item);
}
// Auto detect current. // Auto detect current.
autoDetectDebuggers(); autoDetectCdbDebuggers();
autoDetectGdbOrLldbDebuggers();
// Add debuggers from pre-3.x profiles.xml // Add debuggers from pre-3.x profiles.xml
readLegacyDebuggers(); readLegacyDebuggers();
@@ -639,72 +670,6 @@ void DebuggerItemManager::setItemData(const QVariant &id, const QString &display
} }
} }
QVariant DebuggerItemManager::defaultDebugger(ToolChain *tc)
{
QTC_ASSERT(tc, return QVariant());
DebuggerItem result;
result.setAutoDetected(true);
result.setDisplayName(tr("Auto-detected for Tool Chain %1").arg(tc->displayName()));
Abi abi = Abi::hostAbi();
if (tc)
abi = tc->targetAbi();
// if (abis.first().wordWidth() == 32)
// result.first = cdb.toString();
// else if (abis.first().wordWidth() == 64)
// result.second = cdb.toString();
// // prefer 64bit debugger, even for 32bit binaries:
// if (!result.second.isEmpty())
// result.first = result.second;
foreach (const DebuggerItem &item, m_debuggers)
foreach (const Abi targetAbi, item.abis())
if (targetAbi.isCompatibleWith(abi))
return item.id();
return QVariant();
/*
// CDB for windows:
if (abi.os() == Abi::WindowsOS && abi.osFlavor() != Abi::WindowsMSysFlavor) {
QPair<QString, QString> cdbs = autoDetectCdbDebugger();
result.command = FileName::fromString(abi.wordWidth() == 32 ? cdbs.first : cdbs.second);
result.engineType = CdbEngineType;
return maybeAddDebugger(result, false);
}
// Check suggestions from the SDK.
Environment env = Environment::systemEnvironment();
if (tc) {
tc->addToEnvironment(env); // Find MinGW gdb in toolchain environment.
QString path = tc->suggestedDebugger().toString();
if (!path.isEmpty()) {
const QFileInfo fi(path);
if (!fi.isAbsolute())
path = env.searchInPath(path);
result.command = FileName::fromString(path);
result.engineType = engineTypeFromBinary(path);
return maybeAddDebugger(result, false);
}
}
// Default to GDB, system GDB
result.engineType = GdbEngineType;
QString gdb;
const QString systemGdb = QLatin1String("gdb");
// MinGW: Search for the python-enabled gdb first.
if (abi.os() == Abi::WindowsOS && abi.osFlavor() == Abi::WindowsMSysFlavor)
gdb = env.searchInPath(QLatin1String("gdb-i686-pc-mingw32"));
if (gdb.isEmpty())
gdb = env.searchInPath(systemGdb);
result.command = FileName::fromString(env.searchInPath(gdb.isEmpty() ? systemGdb : gdb));
return maybeAddDebugger(result, false);
*/
}
namespace Internal { namespace Internal {
static QList<QStandardItem *> describeItem(const DebuggerItem &item) static QList<QStandardItem *> describeItem(const DebuggerItem &item)
@@ -946,7 +911,9 @@ void DebuggerKitConfigWidget::manageDebuggers()
void DebuggerKitConfigWidget::currentDebuggerChanged(int) void DebuggerKitConfigWidget::currentDebuggerChanged(int)
{ {
m_kit->setValue(DebuggerKitInformation::id(), m_comboBox->itemData(m_comboBox->currentIndex())); int currentIndex = m_comboBox->currentIndex();
QVariant id = m_comboBox->itemData(currentIndex);
m_kit->setValue(DebuggerKitInformation::id(), id);
} }
void DebuggerKitConfigWidget::onDebuggerAdded(const QVariant &id, const QString &displayName) void DebuggerKitConfigWidget::onDebuggerAdded(const QVariant &id, const QString &displayName)

View File

@@ -185,6 +185,42 @@ void DebuggerItem::setAbi(const Abi &abi)
m_abis.append(abi); m_abis.append(abi);
} }
static DebuggerItem::MatchLevel matchSingle(const Abi &debuggerAbi, const Abi &targetAbi)
{
if (debuggerAbi.architecture() != targetAbi.architecture())
return DebuggerItem::DoesNotMatch;
if (debuggerAbi.os() != targetAbi.os())
return DebuggerItem::DoesNotMatch;
if (debuggerAbi.binaryFormat() != targetAbi.binaryFormat())
return DebuggerItem::DoesNotMatch;
if (debuggerAbi.wordWidth() != targetAbi.wordWidth())
return DebuggerItem::DoesNotMatch;
if (debuggerAbi.os() == Abi::WindowsOS) {
if (debuggerAbi.osFlavor() == Abi::WindowsMSysFlavor && targetAbi.osFlavor() != Abi::WindowsMSysFlavor)
return DebuggerItem::DoesNotMatch;
if (debuggerAbi.osFlavor() != Abi::WindowsMSysFlavor && targetAbi.osFlavor() == Abi::WindowsMSysFlavor)
return DebuggerItem::DoesNotMatch;
return DebuggerItem::MatchesSomewhat;
}
return DebuggerItem::MatchesPerfectly;
}
DebuggerItem::MatchLevel DebuggerItem::matchTarget(const Abi &targetAbi) const
{
MatchLevel bestMatch = DoesNotMatch;
foreach (const Abi &debuggerAbi, m_abis) {
MatchLevel currentMatch = matchSingle(debuggerAbi, targetAbi);
if (currentMatch > bestMatch)
bestMatch = currentMatch;
}
return bestMatch;
}
bool Debugger::DebuggerItem::isValid() const bool Debugger::DebuggerItem::isValid() const
{ {
return m_engineType != NoEngineType; return m_engineType != NoEngineType;

View File

@@ -77,6 +77,9 @@ public:
void setAbis(const QList<ProjectExplorer::Abi> &abis); void setAbis(const QList<ProjectExplorer::Abi> &abis);
void setAbi(const ProjectExplorer::Abi &abi); void setAbi(const ProjectExplorer::Abi &abi);
enum MatchLevel { DoesNotMatch, MatchesSomewhat, MatchesPerfectly };
MatchLevel matchTarget(const ProjectExplorer::Abi &targetAbi) const;
QStringList abiNames() const; QStringList abiNames() const;
private: private:
@@ -113,7 +116,6 @@ public:
static const DebuggerItem *findByCommand(const Utils::FileName &command); static const DebuggerItem *findByCommand(const Utils::FileName &command);
static const DebuggerItem *findById(const QVariant &id); static const DebuggerItem *findById(const QVariant &id);
static QVariant defaultDebugger(ProjectExplorer::ToolChain *tc);
static void restoreDebuggers(); static void restoreDebuggers();
static QString uniqueDisplayName(const QString &base); static QString uniqueDisplayName(const QString &base);
static void setItemData(const QVariant &id, const QString& displayName, const Utils::FileName &fileName); static void setItemData(const QVariant &id, const QString& displayName, const Utils::FileName &fileName);
@@ -126,8 +128,8 @@ public slots:
private: private:
explicit DebuggerItemManager(QObject *parent = 0); explicit DebuggerItemManager(QObject *parent = 0);
static void autoDetectDebuggers(); static void autoDetectGdbOrLldbDebuggers();
static void autoDetectCdbDebugger(); static void autoDetectCdbDebuggers();
static void readLegacyDebuggers(); static void readLegacyDebuggers();
static Utils::PersistentSettingsWriter *m_writer; static Utils::PersistentSettingsWriter *m_writer;