ClangCodeModel: Check system for clangd suitability on first run

Turn off clangd by default if we think the system does not have enough
memory. Inform the user and let them override our decision.

Task-number: QTCREATORBUG-19297
Change-Id: Ib9715c2f089c10d7a2a559a25180e9a943c118b1
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Christian Kandeler
2022-06-17 11:58:14 +02:00
parent 05a7f93f96
commit ea868c8b46
5 changed files with 95 additions and 4 deletions

View File

@@ -31,6 +31,10 @@
#include <QOpenGLContext>
#endif
#ifdef Q_OS_LINUX
#include <sys/sysinfo.h>
#endif
#ifdef Q_OS_WIN
#include <qt_windows.h>
#endif
@@ -111,3 +115,27 @@ bool HostOsInfo::canCreateOpenGLContext(QString *errorMessage)
return canCreate;
#endif
}
optional<quint64> HostOsInfo::totalMemoryInstalledInBytes()
{
#ifdef Q_OS_LINUX
struct sysinfo info;
if (sysinfo(&info) == -1)
return {};
return info.totalram;
#elif defined(Q_OS_WIN)
MEMORYSTATUSEX statex;
statex.dwLength = sizeof statex;
if (!GlobalMemoryStatusEx(&statex))
return {};
return statex.ullTotalPhys;
#elif defined(Q_OS_MACOS)
int mib[] = {CTL_HW, HW_MEMSIZE};
int64_t ram;
size_t length = sizeof(int64_t);
if (sysctl(mib, 2, &ram, &length, nullptr, 0) == -1)
return {};
return ram;
#endif
return {};
}

View File

@@ -27,6 +27,7 @@
#include "utils_global.h"
#include "optional.h"
#include "osspecificaspects.h"
QT_BEGIN_NAMESPACE
@@ -104,6 +105,8 @@ public:
static bool canCreateOpenGLContext(QString *errorMessage);
static optional<quint64> totalMemoryInstalledInBytes();
private:
static Qt::CaseSensitivity m_overrideFileNameCaseSensitivity;
static bool m_useOverrideFileNameCaseSensitivity;

View File

@@ -62,10 +62,12 @@
#include <projectexplorer/taskhub.h>
#include <utils/algorithm.h>
#include <utils/infobar.h>
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <QApplication>
#include <QLabel>
#include <QMenu>
#include <QTextBlock>
#include <QTimer>
@@ -124,6 +126,40 @@ static Client *clientForGeneratedFile(const Utils::FilePath &filePath)
return nullptr;
}
static void checkSystemForClangdSuitability()
{
if (ClangdSettings::haveCheckedHardwareRequirements())
return;
if (ClangdSettings::hardwareFulfillsRequirements())
return;
ClangdSettings::setUseClangd(false);
const QString warnStr = ClangModelManagerSupport::tr("The use of clangd for the C/C++ "
"code model was disabled, because it is likely that its memory requirements "
"would be higher than what your system can handle.");
const Utils::Id clangdWarningSetting("WarnAboutClangd");
Utils::InfoBarEntry info(clangdWarningSetting, warnStr);
info.setDetailsWidgetCreator([] {
const auto label = new QLabel(ClangModelManagerSupport::tr(
"With clangd enabled, Qt Creator fully supports modern C++ "
"when highlighting code, completing symbols and so on.<br>"
"This comes at a higher cost in terms of CPU load and memory usage compared "
"to the built-in code model, which therefore might be the better choice "
"on older machines and/or with legacy code.<br>"
"You can enable/disable and fine-tune clangd <a href=\"dummy\">here</a>."));
label->setWordWrap(true);
QObject::connect(label, &QLabel::linkActivated, [] {
Core::ICore::showOptionsDialog(CppEditor::Constants::CPP_CLANGD_SETTINGS_ID);
});
return label;
});
info.addCustomButton(ClangModelManagerSupport::tr("Enable Anyway"), [clangdWarningSetting] {
ClangdSettings::setUseClangd(true);
Core::ICore::infoBar()->removeInfo(clangdWarningSetting);
});
Core::ICore::infoBar()->addInfo(info);
}
ClangModelManagerSupport::ClangModelManagerSupport()
{
QTC_CHECK(!m_instance);
@@ -132,6 +168,7 @@ ClangModelManagerSupport::ClangModelManagerSupport()
watchForExternalChanges();
watchForInternalChanges();
setupClangdConfigFile();
checkSystemForClangdSuitability();
cppModelManager()->setCurrentDocumentFilter(std::make_unique<ClangdCurrentDocumentFilter>());
cppModelManager()->setLocatorFilter(std::make_unique<ClangGlobalSymbolFilter>());
cppModelManager()->setClassesFilter(std::make_unique<ClangClassesFilter>());

View File

@@ -34,6 +34,7 @@
#include <projectexplorer/session.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/settingsutils.h>
@@ -81,6 +82,7 @@ static QString clangdSizeThresholdKey() { return QLatin1String("ClangdSizeThresh
static QString clangdUseGlobalSettingsKey() { return QLatin1String("useGlobalSettings"); }
static QString sessionsWithOneClangdKey() { return QLatin1String("SessionsWithOneClangd"); }
static QString diagnosticConfigIdKey() { return QLatin1String("diagnosticConfigId"); }
static QString checkedHardwareKey() { return QLatin1String("checkedHardware"); }
static FilePath g_defaultClangdFilePath;
static FilePath fallbackClangdFilePath()
@@ -206,6 +208,22 @@ bool ClangdSettings::useClangd() const
return m_data.useClangd && clangdVersion() >= QVersionNumber(14);
}
void ClangdSettings::setUseClangd(bool use) { instance().m_data.useClangd = use; }
bool ClangdSettings::hardwareFulfillsRequirements()
{
instance().m_data.haveCheckedHardwareReqirements = true;
instance().saveSettings();
const quint64 minRam = quint64(12) * 1024 * 1024 * 1024;
const Utils::optional<quint64> totalRam = Utils::HostOsInfo::totalMemoryInstalledInBytes();
return !totalRam || *totalRam >= minRam;
}
bool ClangdSettings::haveCheckedHardwareRequirements()
{
return instance().data().haveCheckedHardwareReqirements;
}
void ClangdSettings::setDefaultClangdPath(const FilePath &filePath)
{
g_defaultClangdFilePath = filePath;
@@ -350,8 +368,6 @@ void ClangdSettings::saveSettings()
}
#ifdef WITH_TESTS
void ClangdSettings::setUseClangd(bool use) { instance().m_data.useClangd = use; }
void ClangdSettings::setClangdFilePath(const FilePath &filePath)
{
instance().m_data.executableFilePath = filePath;
@@ -435,6 +451,7 @@ QVariantMap ClangdSettings::Data::toMap() const
map.insert(clangdSizeThresholdKey(), sizeThresholdInKb);
map.insert(sessionsWithOneClangdKey(), sessionsWithOneClangd);
map.insert(diagnosticConfigIdKey(), diagnosticConfigId.toSetting());
map.insert(checkedHardwareKey(), true);
return map;
}
@@ -451,6 +468,7 @@ void ClangdSettings::Data::fromMap(const QVariantMap &map)
sessionsWithOneClangd = map.value(sessionsWithOneClangdKey()).toStringList();
diagnosticConfigId = Id::fromSetting(map.value(diagnosticConfigIdKey(),
initialClangDiagnosticConfigId().toSetting()));
haveCheckedHardwareReqirements = map.value(checkedHardwareKey(), false).toBool();
}
} // namespace CppEditor

View File

@@ -111,7 +111,8 @@ public:
&& s1.autoIncludeHeaders == s2.autoIncludeHeaders
&& s1.documentUpdateThreshold == s2.documentUpdateThreshold
&& s1.sizeThresholdEnabled == s2.sizeThresholdEnabled
&& s1.sizeThresholdInKb == s2.sizeThresholdInKb;
&& s1.sizeThresholdInKb == s2.sizeThresholdInKb
&& s1.haveCheckedHardwareReqirements == s2.haveCheckedHardwareReqirements;
}
friend bool operator!=(const Data &s1, const Data &s2) { return !(s1 == s2); }
@@ -126,12 +127,17 @@ public:
bool enableIndexing = true;
bool autoIncludeHeaders = false;
bool sizeThresholdEnabled = false;
bool haveCheckedHardwareReqirements = false;
};
ClangdSettings(const Data &data) : m_data(data) {}
static ClangdSettings &instance();
bool useClangd() const;
static void setUseClangd(bool use);
static bool hardwareFulfillsRequirements();
static bool haveCheckedHardwareRequirements();
static void setDefaultClangdPath(const Utils::FilePath &filePath);
static void setCustomDiagnosticConfigs(const ClangDiagnosticConfigs &configs);
@@ -159,7 +165,6 @@ public:
static Utils::FilePath clangdUserConfigFilePath();
#ifdef WITH_TESTS
static void setUseClangd(bool use);
static void setClangdFilePath(const Utils::FilePath &filePath);
#endif