[clangd] Record memory usages after each notification

Depends on D88415

Differential Revision: https://reviews.llvm.org/D88417
This commit is contained in:
Kadir Cetinkaya 2020-09-28 15:09:55 +02:00
parent 20f69ccfe6
commit 35871fde55
No known key found for this signature in database
GPG Key ID: E39E36B8D2057ED6
5 changed files with 80 additions and 5 deletions

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "ClangdLSPServer.h"
#include "ClangdServer.h"
#include "CodeComplete.h"
#include "Diagnostics.h"
#include "DraftStore.h"
@ -18,6 +19,7 @@
#include "URI.h"
#include "refactor/Tweak.h"
#include "support/Context.h"
#include "support/MemoryTree.h"
#include "support/Trace.h"
#include "clang/Basic/Version.h"
#include "clang/Tooling/Core/Replacement.h"
@ -26,6 +28,7 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
@ -33,6 +36,7 @@
#include "llvm/Support/Path.h"
#include "llvm/Support/SHA1.h"
#include "llvm/Support/ScopedPrinter.h"
#include <chrono>
#include <cstddef>
#include <memory>
#include <mutex>
@ -144,7 +148,6 @@ llvm::Error validateEdits(const DraftStore &DraftMgr, const FileEdits &FE) {
return error("Files must be saved first: {0} (and {1} others)",
LastInvalidFile, InvalidFileCount - 1);
}
} // namespace
// MessageHandler dispatches incoming LSP messages.
@ -163,14 +166,16 @@ public:
log("<-- {0}", Method);
if (Method == "exit")
return false;
if (!Server.Server)
if (!Server.Server) {
elog("Notification {0} before initialization", Method);
else if (Method == "$/cancelRequest")
} else if (Method == "$/cancelRequest") {
onCancel(std::move(Params));
else if (auto Handler = Notifications.lookup(Method))
} else if (auto Handler = Notifications.lookup(Method)) {
Handler(std::move(Params));
else
Server.maybeExportMemoryProfile();
} else {
log("unhandled notification {0}", Method);
}
return true;
}
@ -1234,6 +1239,25 @@ void ClangdLSPServer::publishDiagnostics(
notify("textDocument/publishDiagnostics", Params);
}
void ClangdLSPServer::maybeExportMemoryProfile() {
if (!trace::enabled())
return;
// Profiling might be expensive, so we throttle it to happen once every 5
// minutes.
static constexpr auto ProfileInterval = std::chrono::minutes(5);
auto Now = std::chrono::steady_clock::now();
if (Now < NextProfileTime)
return;
static constexpr trace::Metric MemoryUsage(
"memory_usage", trace::Metric::Value, "component_name");
trace::Span Tracer("ProfileBrief");
MemoryTree MT;
profile(MT);
record(MT, "clangd_lsp_server", MemoryUsage);
NextProfileTime = Now + ProfileInterval;
}
// FIXME: This function needs to be properly tested.
void ClangdLSPServer::onChangeConfiguration(
const DidChangeConfigurationParams &Params) {
@ -1404,6 +1428,9 @@ ClangdLSPServer::ClangdLSPServer(class Transport &Transp,
if (Opts.FoldingRanges)
MsgHandler->bind("textDocument/foldingRange", &ClangdLSPServer::onFoldingRange);
// clang-format on
// Delay first profile until we've finished warming up.
NextProfileTime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
}
ClangdLSPServer::~ClangdLSPServer() {
@ -1424,6 +1451,11 @@ bool ClangdLSPServer::run() {
return CleanExit && ShutdownRequestReceived;
}
void ClangdLSPServer::profile(MemoryTree &MT) const {
if (Server)
Server->profile(MT.child("clangd_server"));
}
std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File,
const clangd::Diagnostic &D) {
std::lock_guard<std::mutex> Lock(FixItsMutex);

View File

@ -17,11 +17,13 @@
#include "Protocol.h"
#include "Transport.h"
#include "support/Context.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/JSON.h"
#include <chrono>
#include <memory>
namespace clang {
@ -67,6 +69,9 @@ public:
/// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence.
bool run();
/// Profiles resource-usage.
void profile(MemoryTree &MT) const;
private:
// Implement ClangdServer::Callbacks.
void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
@ -160,6 +165,14 @@ private:
/// Sends a "publishDiagnostics" notification to the LSP client.
void publishDiagnostics(const PublishDiagnosticsParams &);
/// Runs profiling and exports memory usage metrics if tracing is enabled and
/// profiling hasn't happened recently.
void maybeExportMemoryProfile();
/// Timepoint until which profiling is off. It is used to throttle profiling
/// requests.
std::chrono::steady_clock::time_point NextProfileTime;
/// Since initialization of CDBs and ClangdServer is done lazily, the
/// following context captures the one used while creating ClangdLSPServer and
/// passes it to above mentioned object instances to make sure they share the

View File

@ -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"
@ -826,5 +827,12 @@ ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) {
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

View File

@ -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 @@ public:
LLVM_NODISCARD bool
blockUntilIdleForTest(llvm::Optional<double> 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<tooling::Range> Ranges,

View File

@ -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 clangd {
namespace {
using ::testing::AllOf;
using ::testing::Contains;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::Gt;
@ -1236,6 +1239,21 @@ TEST(ClangdServer, TidyOverrideTest) {
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