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 @@ -163,14 +163,23 @@ log("<-- {0}", Method); if (Method == "exit") return false; - if (!Server.Server) + if (!Server.Server) { elog("Notification {0} before initialization", Method); - else if (Method == "$/cancelRequest") + return true; + } + if (Method == "$/cancelRequest") onCancel(std::move(Params)); else if (auto Handler = Notifications.lookup(Method)) Handler(std::move(Params)); else log("unhandled notification {0}", Method); + + // Record memory usage after each memory usage. This currently takes <1ms, + // so it is safe to do frequently. + trace::Span Tracer("RecordMemoryUsage"); + MemoryTree MT; + Server.Server->attachMemoryUsage(MT); + MT.traverseTree(trace::recordMemoryUsage, "clangd_server"); return true; } diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -25,6 +25,7 @@ #include "refactor/Tweak.h" #include "support/Cancellation.h" #include "support/Function.h" +#include "support/MemoryTree.h" #include "support/ThreadsafeFS.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Core/Replacement.h" @@ -337,6 +338,9 @@ LLVM_NODISCARD bool blockUntilIdleForTest(llvm::Optional TimeoutSeconds = 10); + /// Builds a nested representation of memory used by components. + void attachMemoryUsage(MemoryTree &MT) const; + private: void formatCode(PathRef File, llvm::StringRef Code, ArrayRef Ranges, diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -28,6 +28,7 @@ #include "refactor/Tweak.h" #include "support/Logger.h" #include "support/Markup.h" +#include "support/MemoryTree.h" #include "support/ThreadsafeFS.h" #include "support/Trace.h" #include "clang/Format/Format.h" @@ -833,5 +834,12 @@ BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds)); } +void ClangdServer::attachMemoryUsage(MemoryTree &MT) const { + if (DynamicIdx) + DynamicIdx->attachMemoryUsage(*MT.addChild("dynamic_index")); + if (BackgroundIdx) + BackgroundIdx->attachMemoryUsage(*MT.addChild("background_index")); + WorkScheduler.attachMemoryUsage(*MT.addChild("tuscheduler")); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -17,6 +17,7 @@ #include "TestFS.h" #include "TestTU.h" #include "URI.h" +#include "support/MemoryTree.h" #include "support/Path.h" #include "support/Threading.h" #include "clang/Config/config.h" @@ -27,6 +28,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" @@ -48,6 +50,7 @@ namespace { using ::testing::AllOf; +using ::testing::Contains; using ::testing::ElementsAre; using ::testing::Field; using ::testing::Gt; @@ -1236,6 +1239,31 @@ EXPECT_FALSE(DiagConsumer.HadDiagsInLastCallback); } +TEST(ClangdServer, MemoryUsageTest) { + MockFS FS; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + + std::vector SeenComponents; + auto CB = [&SeenComponents](size_t Size, llvm::StringRef CompName) { + SeenComponents.emplace_back(CompName.str()); + }; + + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + Server.attachMemoryUsage(MT); + MT.traverseTree(CB, "clangd_server"); + EXPECT_THAT(SeenComponents, Contains("clangd_server")); + SeenComponents.clear(); + + auto FooCpp = testPath("foo.cpp"); + Server.addDocument(FooCpp, ""); + ASSERT_TRUE(Server.blockUntilIdleForTest()); + Server.attachMemoryUsage(MT); + MT.traverseTree(CB, "clangd_server"); + EXPECT_THAT(SeenComponents, + Contains("clangd_server.tuscheduler.preambles." + FooCpp)); +} } // namespace } // namespace clangd } // namespace clang