diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index d46147ac89cd8a..99c2465a579c01 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -36,8 +36,11 @@ #include "llvm/Support/Path.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/raw_ostream.h" #include #include +#include +#include #include #include #include @@ -617,6 +620,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params, ExecuteCommandParams::CLANGD_APPLY_TWEAK}}, }}, {"typeHierarchyProvider", true}, + {"memoryUsageProvider", true}, // clangd extension. }}}}; if (Opts.Encoding) Result["offsetEncoding"] = *Opts.Encoding; @@ -1383,6 +1387,14 @@ void ClangdLSPServer::onSemanticTokensDelta( }); } +void ClangdLSPServer::onMemoryUsage(const NoParams &, + Callback Reply) { + llvm::BumpPtrAllocator DetailAlloc; + MemoryTree MT(&DetailAlloc); + profile(MT); + Reply(std::move(MT)); +} + ClangdLSPServer::ClangdLSPServer(class Transport &Transp, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts) @@ -1425,6 +1437,7 @@ ClangdLSPServer::ClangdLSPServer(class Transport &Transp, MsgHandler->bind("textDocument/documentLink", &ClangdLSPServer::onDocumentLink); MsgHandler->bind("textDocument/semanticTokens/full", &ClangdLSPServer::onSemanticTokens); MsgHandler->bind("textDocument/semanticTokens/full/delta", &ClangdLSPServer::onSemanticTokensDelta); + MsgHandler->bind("$/memoryUsage", &ClangdLSPServer::onMemoryUsage); if (Opts.FoldingRanges) MsgHandler->bind("textDocument/foldingRange", &ClangdLSPServer::onFoldingRange); // clang-format on diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index 7054c48652c568..9fb23a07f6c50c 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -24,6 +24,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/Support/JSON.h" #include +#include #include namespace clang { @@ -141,6 +142,9 @@ class ClangdLSPServer : private ClangdServer::Callbacks { void onSemanticTokens(const SemanticTokensParams &, Callback); void onSemanticTokensDelta(const SemanticTokensDeltaParams &, Callback); + /// This is a clangd extension. Provides a json tree representing memory usage + /// hierarchy. + void onMemoryUsage(const NoParams &, Callback); std::vector getFixes(StringRef File, const clangd::Diagnostic &D); diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index f41cea28fea35b..0b89c622e6f836 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -1300,5 +1300,17 @@ llvm::json::Value toJSON(const FoldingRange &Range) { return Result; } +llvm::json::Value toJSON(const MemoryTree &MT) { + llvm::json::Object Out; + int64_t Total = MT.self(); + Out["_self"] = Total; + for (const auto &Entry : MT.children()) { + auto Child = toJSON(Entry.getSecond()); + Total += *Child.getAsObject()->getInteger("_total"); + Out[Entry.first] = std::move(Child); + } + Out["_total"] = Total; + return Out; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index 6f395ffb21c535..165a4a89e1cd0d 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -25,6 +25,7 @@ #include "URI.h" #include "index/SymbolID.h" +#include "support/MemoryTree.h" #include "clang/Index/IndexSymbol.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/JSON.h" @@ -1576,6 +1577,27 @@ struct FoldingRange { }; llvm::json::Value toJSON(const FoldingRange &Range); +/// Keys starting with an underscore(_) represent leaves, e.g. _total or _self +/// for memory usage of whole subtree or only that specific node in bytes. All +/// other keys represents children. An example: +/// { +/// "_self": 0, +/// "_total": 8, +/// "child1": { +/// "_self": 4, +/// "_total": 4, +/// } +/// "child2": { +/// "_self": 2, +/// "_total": 4, +/// "child_deep": { +/// "_self": 2, +/// "_total": 2, +/// } +/// } +/// } +llvm::json::Value toJSON(const MemoryTree &MT); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test index 4125c27e4e35a8..8f4d0653bb786f 100644 --- a/clang-tools-extra/clangd/test/initialize-params.test +++ b/clang-tools-extra/clangd/test/initialize-params.test @@ -66,6 +66,7 @@ # CHECK-NEXT: ] # CHECK-NEXT: }, # CHECK-NEXT: "hoverProvider": true, +# CHECK-NEXT: "memoryUsageProvider": true # CHECK-NEXT: "referencesProvider": true, # CHECK-NEXT: "renameProvider": true, # CHECK-NEXT: "selectionRangeProvider": true, diff --git a/clang-tools-extra/clangd/test/memory_tree.test b/clang-tools-extra/clangd/test/memory_tree.test new file mode 100644 index 00000000000000..41efdfb49e7a93 --- /dev/null +++ b/clang-tools-extra/clangd/test/memory_tree.test @@ -0,0 +1,81 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"void func() {}"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"$/memoryUsage","params":{}} +# CHECK: "id": 1, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}}, +# CHECK-NEXT: "clangd_server": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}}, +# CHECK-NEXT: "dynamic_index": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}}, +# CHECK-NEXT: "main_file": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}}, +# CHECK-NEXT: "index": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: }, +# CHECK-NEXT: "symbols": { +# CHECK-NEXT: "{{.*}}main.cpp": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}}, +# CHECK-NEXT: "references": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: }, +# CHECK-NEXT: "relations": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: }, +# CHECK-NEXT: "symbols": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "preamble": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}}, +# CHECK-NEXT: "index": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: }, +# CHECK-NEXT: "symbols": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "tuscheduler": { +# CHECK-NEXT: "{{.*}}main.cpp": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}}, +# CHECK-NEXT: "ast": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: }, +# CHECK-NEXT: "preamble": { +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "_self": {{[0-9]+}}, +# CHECK-NEXT: "_total": {{[0-9]+}} +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} +