diff --git a/clang-tools-extra/clangd/index/remote/CMakeLists.txt b/clang-tools-extra/clangd/index/remote/CMakeLists.txt --- a/clang-tools-extra/clangd/index/remote/CMakeLists.txt +++ b/clang-tools-extra/clangd/index/remote/CMakeLists.txt @@ -1,5 +1,7 @@ if (CLANGD_ENABLE_REMOTE) generate_protos(RemoteIndexProto "Index.proto") + generate_protos(MonitoringServiceProto "MonitoringService.proto" + GRPC) generate_protos(RemoteIndexServiceProto "Service.proto" DEPENDS "Index.proto" GRPC) @@ -8,6 +10,7 @@ target_link_libraries(RemoteIndexServiceProto PRIVATE RemoteIndexProto + MonitoringServiceProto ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../) diff --git a/clang-tools-extra/clangd/index/remote/MonitoringService.proto b/clang-tools-extra/clangd/index/remote/MonitoringService.proto new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/index/remote/MonitoringService.proto @@ -0,0 +1,27 @@ +//===--- MonitoringService.proto - CLangd Remote index monitoring service -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +syntax = "proto2"; + +package clang.clangd.remote.v1; + +message MonitoringInfoRequest {} +message MonitoringInfoReply { + // Time since the server started (in seconds). + optional uint64 uptime_seconds = 1; + // Time since the index was built on the indexing machine. + optional uint64 index_age_seconds = 2; + // ID of the indexed commit in Version Control System. + optional string index_commit_hash = 3; + // URL to the index file. + optional string index_link = 4; +} + +service Monitor { + rpc MonitoringInfo(MonitoringInfoRequest) returns (MonitoringInfoReply) {} +} diff --git a/clang-tools-extra/clangd/index/remote/Service.proto b/clang-tools-extra/clangd/index/remote/Service.proto --- a/clang-tools-extra/clangd/index/remote/Service.proto +++ b/clang-tools-extra/clangd/index/remote/Service.proto @@ -23,4 +23,3 @@ rpc Relations(RelationsRequest) returns (stream RelationsReply) {} } - diff --git a/clang-tools-extra/clangd/index/remote/server/Server.cpp b/clang-tools-extra/clangd/index/remote/server/Server.cpp --- a/clang-tools-extra/clangd/index/remote/server/Server.cpp +++ b/clang-tools-extra/clangd/index/remote/server/Server.cpp @@ -8,7 +8,10 @@ #include "Features.inc" #include "Index.pb.h" +#include "MonitoringService.grpc.pb.h" +#include "MonitoringService.pb.h" #include "Service.grpc.pb.h" +#include "Service.pb.h" #include "index/Index.h" #include "index/Serialization.h" #include "index/Symbol.h" @@ -288,11 +291,44 @@ clangd::SymbolIndex &Index; }; +class Monitor final : public v1::Monitor::Service { +public: + Monitor(llvm::sys::TimePoint<> StartTime) + : StartTime(StartTime), IndexLastReload(StartTime) {} + + void updateIndex(llvm::sys::TimePoint<> UpdateTime) { + IndexLastReload.exchange(UpdateTime); + } + +private: + // FIXME(kirillbobyrev): Most fields should be populated when the index + // reloads (probably in adjacent metadata.txt file next to loaded .idx) but + // they aren't right now. + grpc::Status MonitoringInfo(grpc::ServerContext *Context, + const v1::MonitoringInfoRequest *Request, + v1::MonitoringInfoReply *Reply) override { + Reply->set_uptime_seconds(std::chrono::duration_cast( + std::chrono::system_clock::now() - StartTime) + .count()); + // FIXME(kirillbobyrev): This is not really the time an index was built, + // this is just the time since last index reload. + Reply->set_index_age_seconds( + std::chrono::duration_cast( + std::chrono::system_clock::now() - IndexLastReload.load()) + .count()); + return grpc::Status::OK; + } + + const llvm::sys::TimePoint<> StartTime; + std::atomic> IndexLastReload; +}; + // Detect changes in \p IndexPath file and load new versions of the index // whenever they become available. void hotReload(clangd::SwapIndex &Index, llvm::StringRef IndexPath, llvm::vfs::Status &LastStatus, - llvm::IntrusiveRefCntPtr &FS) { + llvm::IntrusiveRefCntPtr &FS, + Monitor &Monitor) { auto Status = FS->status(IndexPath); // Requested file is same as loaded index: no reload is needed. if (!Status || (Status->getLastModificationTime() == @@ -309,12 +345,13 @@ return; } Index.reset(std::move(NewIndex)); + Monitor.updateIndex(Status->getLastModificationTime()); log("New index version loaded. Last modification time: {0}, size: {1} bytes.", Status->getLastModificationTime(), Status->getSize()); } void runServerAndWait(clangd::SymbolIndex &Index, llvm::StringRef ServerAddress, - llvm::StringRef IndexPath) { + llvm::StringRef IndexPath, Monitor &Monitor) { RemoteIndexServer Service(Index, IndexRoot); grpc::EnableDefaultHealthCheckService(true); @@ -327,6 +364,7 @@ Builder.AddChannelArgument(GRPC_ARG_MAX_CONNECTION_IDLE_MS, IdleTimeoutSeconds * 1000); Builder.RegisterService(&Service); + Builder.RegisterService(&Monitor); std::unique_ptr Server(Builder.BuildAndStart()); log("Server listening on {0}", ServerAddress); @@ -425,16 +463,18 @@ } clang::clangd::SwapIndex Index(std::move(SymIndex)); - std::thread HotReloadThread([&Index, &Status, &FS]() { + Monitor Monitor(Status->getLastModificationTime()); + + std::thread HotReloadThread([&Index, &Status, &FS, &Monitor]() { llvm::vfs::Status LastStatus = *Status; static constexpr auto RefreshFrequency = std::chrono::seconds(30); while (!clang::clangd::shutdownRequested()) { - hotReload(Index, llvm::StringRef(IndexPath), LastStatus, FS); + hotReload(Index, llvm::StringRef(IndexPath), LastStatus, FS, Monitor); std::this_thread::sleep_for(RefreshFrequency); } }); - runServerAndWait(Index, ServerAddress, IndexPath); + runServerAndWait(Index, ServerAddress, IndexPath, Monitor); HotReloadThread.join(); }