From 03e6351c0e13317ec3ab51e97fd17da136413bbd Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 16 Nov 2021 14:12:41 +0100 Subject: [PATCH] ClangCodeModel: Provide clangd memory usage in language inspector Change-Id: I8a87cb5f267571584b2eecac06be65b841592c7a Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 155 ++++++++++++++++++++ src/plugins/clangcodemodel/clangdclient.h | 1 + 2 files changed, 156 insertions(+) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 7d034f7aac3..fe686a973f0 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -66,19 +66,27 @@ #include #include #include +#include #include +#include #include +#include #include #include #include #include #include +#include +#include #include #include #include +#include +#include #include +#include #include #include #include @@ -1038,6 +1046,22 @@ private: QElapsedTimer m_timer; }; +class MemoryTreeModel; +class MemoryUsageWidget : public QWidget +{ + Q_DECLARE_TR_FUNCTIONS(MemoryUsageWidget) +public: + MemoryUsageWidget(ClangdClient *client); + +private: + void setupUi(); + void getMemoryTree(); + + ClangdClient * const m_client; + MemoryTreeModel * const m_model; + Utils::TreeView m_view; +}; + class ClangdClient::Private { public: @@ -1419,6 +1443,11 @@ void ClangdClient::handleDocumentClosed(TextDocument *doc) d->virtualRanges.remove(doc); } +const LanguageClient::Client::CustomInspectorTabs ClangdClient::createCustomInspectorTabs() +{ + return {std::make_pair(new MemoryUsageWidget(this), tr("Memory Usage"))}; +} + QVersionNumber ClangdClient::versionNumber() const { if (d->versionNumber) @@ -3675,6 +3704,132 @@ bool ClangdClient::FollowSymbolData::defLinkIsAmbiguous() const return cursorNode->mightBeAmbiguousVirtualCall() || cursorNode->isPureVirtualDeclaration(); } +class MemoryTree : public JsonObject +{ +public: + using JsonObject::JsonObject; + + // number of bytes used, including child components + qint64 total() const { return qint64(typedValue(totalKey())); } + + // number of bytes used, excluding child components + qint64 self() const { return qint64(typedValue(selfKey())); } + + // named child components + using NamedComponent = std::pair; + QList children() const + { + QList components; + const auto obj = operator const QJsonObject &(); + for (auto it = obj.begin(); it != obj.end(); ++it) { + if (it.key() == totalKey() || it.key() == selfKey()) + continue; + components << std::make_pair(MemoryTree(it.value()), it.key()); + } + return components; + } + +private: + static QString totalKey() { return QLatin1String("_total"); } + static QString selfKey() { return QLatin1String("_self"); } +}; + +class MemoryTreeItem : public Utils::TreeItem +{ + Q_DECLARE_TR_FUNCTIONS(MemoryTreeItem) +public: + MemoryTreeItem(const QString &displayName, const MemoryTree &tree) + : m_displayName(displayName), m_bytesUsed(tree.total()) + { + for (const MemoryTree::NamedComponent &component : tree.children()) + appendChild(new MemoryTreeItem(component.second, component.first)); + } + +private: + QVariant data(int column, int role) const override + { + switch (role) { + case Qt::DisplayRole: + if (column == 0) + return m_displayName; + return memString(); + case Qt::TextAlignmentRole: + if (column == 1) + return Qt::AlignRight; + break; + default: + break; + } + return {}; + } + + QString memString() const + { + static const QList> factors{ + std::make_pair(1000000000, QString("GB")), + std::make_pair(1000000, QString("MB")), + std::make_pair(1000, QString("KB")), + }; + for (const auto &factor : factors) { + if (m_bytesUsed > factor.first) + return QString::number(qint64(std::round(double(m_bytesUsed) / factor.first))) + + ' ' + factor.second; + } + return QString::number(m_bytesUsed) + " B"; + } + + const QString m_displayName; + const qint64 m_bytesUsed; +}; + +class MemoryTreeModel : public Utils::BaseTreeModel +{ +public: + MemoryTreeModel(QObject *parent) : BaseTreeModel(parent) + { + setHeader({tr("Component"), tr("Total Memory")}); + } + + void update(const MemoryTree &tree) + { + setRootItem(new MemoryTreeItem({}, tree)); + } +}; + +MemoryUsageWidget::MemoryUsageWidget(ClangdClient *client) + : m_client(client), m_model(new MemoryTreeModel(this)) +{ + setupUi(); + getMemoryTree(); +} + +void MemoryUsageWidget::setupUi() +{ + const auto layout = new QVBoxLayout(this); + m_view.setContextMenuPolicy(Qt::CustomContextMenu); + m_view.header()->setSectionResizeMode(QHeaderView::ResizeToContents); + m_view.header()->setStretchLastSection(false); + m_view.setModel(m_model); + layout->addWidget(&m_view); + connect(&m_view, &QWidget::customContextMenuRequested, this, [this](const QPoint &pos) { + QMenu menu; + menu.addAction(tr("Update"), [this] { getMemoryTree(); }); + menu.exec(m_view.mapToGlobal(pos)); + }); +} + +void MemoryUsageWidget::getMemoryTree() +{ + Request request("$/memoryUsage", {}); + request.setResponseCallback([this](decltype(request)::Response response) { + qCDebug(clangdLog) << "received memory usage response"; + if (const auto result = response.result()) + m_model->update(*result); + }); + qCDebug(clangdLog) << "sending memory usage request"; + m_client->sendContent(request, ClangdClient::SendDocUpdates::Ignore); +} + } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index de898696ec0..7a1159b207f 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -102,6 +102,7 @@ private: void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms) override; void handleDocumentOpened(TextEditor::TextDocument *doc) override; void handleDocumentClosed(TextEditor::TextDocument *doc) override; + const CustomInspectorTabs createCustomInspectorTabs() override; class Private; class FollowSymbolData;