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->profile(MT); + trace::recordMemoryUsage(MT, "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 profile(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" @@ -822,5 +823,12 @@ BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds)); } +void ClangdServer::profile(MemoryTree &MT) const { + if (DynamicIdx) + DynamicIdx->profile(MT.child("dynamic_index")); + if (BackgroundIdx) + BackgroundIdx->profile(MT.child("background_index")); + WorkScheduler.profile(MT.child("tuscheduler")); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdLSPServerTests.cpp @@ -164,6 +164,17 @@ stop(); EXPECT_THAT(Tracer.takeMetric("lsp_latency", MethodName), testing::SizeIs(1)); } + +TEST_F(LSPTest, RecordsMemoryUsage) { + trace::TestTracer Tracer; + auto &Client = start(); + EXPECT_THAT(Tracer.takeMetric("memory_usage", "clangd_server"), + testing::SizeIs(0)); + Client.notify("", {}); + stop(); + EXPECT_THAT(Tracer.takeMetric("memory_usage", "clangd_server"), + testing::SizeIs(1)); +} } // namespace } // 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,21 @@ EXPECT_FALSE(DiagConsumer.HadDiagsInLastCallback); } +TEST(ClangdServer, MemoryUsageTest) { + MockFS FS; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, ClangdServer::optsForTest()); + + auto FooCpp = testPath("foo.cpp"); + Server.addDocument(FooCpp, ""); + ASSERT_TRUE(Server.blockUntilIdleForTest()); + + llvm::BumpPtrAllocator Alloc; + MemoryTree MT(&Alloc); + Server.profile(MT); + ASSERT_TRUE(MT.children().count("tuscheduler")); + EXPECT_TRUE(MT.child("tuscheduler").children().count(FooCpp)); +} } // namespace } // namespace clangd } // namespace clang