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 @@ -170,14 +170,22 @@ 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"); + Server.Server->getMemoryUsage().traverseTree(true, 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. + MemoryTree getMemoryUsage() 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 @@ -833,5 +833,14 @@ BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds)); } +MemoryTree ClangdServer::getMemoryUsage() const { + MemoryTree MT; + if (DynamicIdx) + MT.addChild("dynamic_index", DynamicIdx->getMemoryUsage()); + if (BackgroundIdx) + MT.addChild("background_index", BackgroundIdx->getMemoryUsage()); + MT.addChild("tuscheduler", WorkScheduler.getMemoryUsage()); + return MT; +} } // 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 @@ -48,6 +48,7 @@ namespace { using ::testing::AllOf; +using ::testing::Contains; using ::testing::ElementsAre; using ::testing::Field; using ::testing::Gt; @@ -61,6 +62,16 @@ Location{URIForFile::canonicalize(File, testRoot()), Range}; } +MATCHER_P(WithName, Name, "") { + if (arg.Name == Name) + return true; + if (auto *Stream = result_listener->stream()) { + llvm::raw_os_ostream OS(*Stream); + OS << arg.Name; + } + return false; +} + bool diagsContainErrors(const std::vector &Diagnostics) { for (auto D : Diagnostics) { if (D.Severity == DiagnosticsEngine::Error || @@ -1236,6 +1247,35 @@ EXPECT_FALSE(DiagConsumer.HadDiagsInLastCallback); } +TEST(ClangdServer, MemoryUsageTest) { + MockFS FS; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + + struct Component { + std::string Name; + bool Size; + }; + std::vector SeenComponents; + auto CB = [&SeenComponents](size_t Size, llvm::StringRef CompName) { + Component C; + C.Name = CompName.str(); + C.Size = Size; + llvm::errs() << "Got: " << CompName << '\n'; + SeenComponents.emplace_back(std::move(C)); + }; + + Server.getMemoryUsage().traverseTree(false, CB, "clangd_server"); + EXPECT_THAT(SeenComponents, Contains(WithName("clangd_server"))); + + auto FooCpp = testPath("foo.cpp"); + Server.addDocument(FooCpp, ""); + ASSERT_TRUE(Server.blockUntilIdleForTest()); + Server.getMemoryUsage().traverseTree(false, CB, "clangd_server"); + EXPECT_THAT(SeenComponents, + Contains(WithName( + "clangd_server.dynamic_index.preamble_symbols.foo.cpp"))); +} } // namespace } // namespace clangd } // namespace clang