diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- 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,27 @@ void onSemanticTokens(const SemanticTokensParams &, Callback); void onSemanticTokensDelta(const SemanticTokensDeltaParams &, Callback); + /// This is a clangd extension. Provides a json tree representing memory usage + /// hierarchy. 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, + /// } + /// } + /// } + void onDumpMemoryTree(const NoParams &, Callback); std::vector getFixes(StringRef File, const clangd::Diagnostic &D); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- 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 @@ -1383,6 +1386,33 @@ }); } +void ClangdLSPServer::onDumpMemoryTree(const NoParams &, + Callback Reply) { + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + profile(MT); + + std::function DumpTree = + [&](const MemoryTree &MT, llvm::json::Object &Out) { + int64_t Total = MT.self(); + Out["_self"] = Total; + for (const auto &Entry : MT.children()) { + auto *Child = Out.try_emplace(Entry.getFirst(), llvm::json::Object{}) + .first->getSecond() + .getAsObject(); + assert(Child); + Total += DumpTree(Entry.getSecond(), *Child); + } + Out["_total"] = Total; + return Total; + }; + + llvm::json::Object Out; + DumpTree(MT, Out); + + Reply(std::move(Out)); +} + ClangdLSPServer::ClangdLSPServer(class Transport &Transp, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts) @@ -1425,6 +1455,7 @@ MsgHandler->bind("textDocument/documentLink", &ClangdLSPServer::onDocumentLink); MsgHandler->bind("textDocument/semanticTokens/full", &ClangdLSPServer::onSemanticTokens); MsgHandler->bind("textDocument/semanticTokens/full/delta", &ClangdLSPServer::onSemanticTokensDelta); + MsgHandler->bind("$/dumpMemoryTree", &ClangdLSPServer::onDumpMemoryTree); if (Opts.FoldingRanges) MsgHandler->bind("textDocument/foldingRange", &ClangdLSPServer::onFoldingRange); // clang-format on diff --git a/clang-tools-extra/clangd/test/memory_tree.test b/clang-tools-extra/clangd/test/memory_tree.test new file mode 100644 --- /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":"$/dumpMemoryTree","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"} +