655 lines · cpp
1//===--- Server.cpp - gRPC-based Remote Index Server ---------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Feature.h"10#include "Index.pb.h"11#include "MonitoringService.grpc.pb.h"12#include "MonitoringService.pb.h"13#include "Service.grpc.pb.h"14#include "Service.pb.h"15#include "index/Index.h"16#include "index/Serialization.h"17#include "index/Symbol.h"18#include "index/remote/marshalling/Marshalling.h"19#include "support/Context.h"20#include "support/Logger.h"21#include "support/Shutdown.h"22#include "support/ThreadsafeFS.h"23#include "support/Trace.h"24#include "llvm/ADT/IntrusiveRefCntPtr.h"25#include "llvm/ADT/StringRef.h"26#include "llvm/Support/Chrono.h"27#include "llvm/Support/CommandLine.h"28#include "llvm/Support/Error.h"29#include "llvm/Support/FileSystem.h"30#include "llvm/Support/FormatVariadic.h"31#include "llvm/Support/Path.h"32#include "llvm/Support/Signals.h"33#include "llvm/Support/VirtualFileSystem.h"34 35#include <chrono>36#include <grpc++/grpc++.h>37#include <grpc++/health_check_service_interface.h>38#include <memory>39#include <optional>40#include <string>41#include <thread>42#include <utility>43 44#if ENABLE_GRPC_REFLECTION45#include <grpc++/ext/proto_server_reflection_plugin.h>46#endif47 48#ifdef __GLIBC__49#include <malloc.h>50#endif51 52namespace clang {53namespace clangd {54namespace remote {55namespace {56 57static constexpr char Overview[] = R"(58This is an experimental remote index implementation. The server opens Dex and59awaits gRPC lookup requests from the client.60)";61 62llvm::cl::opt<std::string> IndexPath(llvm::cl::desc("<INDEX FILE>"),63 llvm::cl::Positional, llvm::cl::Required);64 65llvm::cl::opt<std::string> IndexRoot(llvm::cl::desc("<PROJECT ROOT>"),66 llvm::cl::Positional, llvm::cl::Required);67 68llvm::cl::opt<Logger::Level> LogLevel{69 "log",70 llvm::cl::desc("Verbosity of log messages written to stderr"),71 values(clEnumValN(Logger::Error, "error", "Error messages only"),72 clEnumValN(Logger::Info, "info", "High level execution tracing"),73 clEnumValN(Logger::Debug, "verbose", "Low level details")),74 llvm::cl::init(Logger::Info),75};76 77llvm::cl::opt<bool> LogPublic{78 "log-public",79 llvm::cl::desc("Avoid logging potentially-sensitive request details"),80 llvm::cl::init(false),81};82 83llvm::cl::opt<std::string> LogPrefix{84 "log-prefix",85 llvm::cl::desc("A string that'll be prepended to all log statements. "86 "Useful when running multiple instances on same host."),87};88 89llvm::cl::opt<std::string> TraceFile(90 "trace-file",91 llvm::cl::desc("Path to the file where tracer logs will be stored"));92 93llvm::cl::opt<bool> PrettyPrint{94 "pretty",95 llvm::cl::desc("Pretty-print JSON output in the trace"),96 llvm::cl::init(false),97};98 99llvm::cl::opt<std::string> ServerAddress(100 "server-address", llvm::cl::init("0.0.0.0:50051"),101 llvm::cl::desc("Address of the invoked server. Defaults to 0.0.0.0:50051"));102 103llvm::cl::opt<size_t> IdleTimeoutSeconds(104 "idle-timeout", llvm::cl::init(8 * 60),105 llvm::cl::desc("Maximum time a channel may stay idle until server closes "106 "the connection, in seconds. Defaults to 480."));107 108llvm::cl::opt<size_t> LimitResults(109 "limit-results", llvm::cl::init(10000),110 llvm::cl::desc("Maximum number of results to stream as a response to "111 "single request. Limit is to keep the server from being "112 "DOS'd. Defaults to 10000."));113 114static Key<grpc::ServerContext *> CurrentRequest;115 116class RemoteIndexServer final : public v1::SymbolIndex::Service {117public:118 RemoteIndexServer(clangd::SymbolIndex &Index, llvm::StringRef IndexRoot)119 : Index(Index) {120 llvm::SmallString<256> NativePath = IndexRoot;121 llvm::sys::path::native(NativePath);122 ProtobufMarshaller = std::unique_ptr<Marshaller>(new Marshaller(123 /*RemoteIndexRoot=*/llvm::StringRef(NativePath),124 /*LocalIndexRoot=*/""));125 }126 127private:128 using stopwatch = std::chrono::steady_clock;129 130 grpc::Status Lookup(grpc::ServerContext *Context,131 const LookupRequest *Request,132 grpc::ServerWriter<LookupReply> *Reply) override {133 auto StartTime = stopwatch::now();134 WithContextValue WithRequestContext(CurrentRequest, Context);135 logRequest(*Request);136 trace::Span Tracer("LookupRequest");137 auto Req = ProtobufMarshaller->fromProtobuf(Request);138 if (!Req) {139 elog("Can not parse LookupRequest from protobuf: {0}", Req.takeError());140 return grpc::Status::CANCELLED;141 }142 unsigned Sent = 0;143 unsigned FailedToSend = 0;144 bool HasMore = false;145 Index.lookup(*Req, [&](const clangd::Symbol &Item) {146 if (Sent >= LimitResults) {147 HasMore = true;148 return;149 }150 auto SerializedItem = ProtobufMarshaller->toProtobuf(Item);151 if (!SerializedItem) {152 elog("Unable to convert Symbol to protobuf: {0}",153 SerializedItem.takeError());154 ++FailedToSend;155 return;156 }157 LookupReply NextMessage;158 *NextMessage.mutable_stream_result() = *SerializedItem;159 logResponse(NextMessage);160 Reply->Write(NextMessage);161 ++Sent;162 });163 if (HasMore)164 log("[public] Limiting result size for Lookup request.");165 LookupReply LastMessage;166 LastMessage.mutable_final_result()->set_has_more(HasMore);167 logResponse(LastMessage);168 Reply->Write(LastMessage);169 SPAN_ATTACH(Tracer, "Sent", Sent);170 SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);171 logRequestSummary("v1/Lookup", Sent, StartTime);172 return grpc::Status::OK;173 }174 175 grpc::Status FuzzyFind(grpc::ServerContext *Context,176 const FuzzyFindRequest *Request,177 grpc::ServerWriter<FuzzyFindReply> *Reply) override {178 auto StartTime = stopwatch::now();179 WithContextValue WithRequestContext(CurrentRequest, Context);180 logRequest(*Request);181 trace::Span Tracer("FuzzyFindRequest");182 auto Req = ProtobufMarshaller->fromProtobuf(Request);183 if (!Req) {184 elog("Can not parse FuzzyFindRequest from protobuf: {0}",185 Req.takeError());186 return grpc::Status::CANCELLED;187 }188 if (!Req->Limit || *Req->Limit > LimitResults) {189 log("[public] Limiting result size for FuzzyFind request from {0} to {1}",190 Req->Limit, LimitResults);191 Req->Limit = LimitResults;192 }193 unsigned Sent = 0;194 unsigned FailedToSend = 0;195 bool HasMore = Index.fuzzyFind(*Req, [&](const clangd::Symbol &Item) {196 auto SerializedItem = ProtobufMarshaller->toProtobuf(Item);197 if (!SerializedItem) {198 elog("Unable to convert Symbol to protobuf: {0}",199 SerializedItem.takeError());200 ++FailedToSend;201 return;202 }203 FuzzyFindReply NextMessage;204 *NextMessage.mutable_stream_result() = *SerializedItem;205 logResponse(NextMessage);206 Reply->Write(NextMessage);207 ++Sent;208 });209 FuzzyFindReply LastMessage;210 LastMessage.mutable_final_result()->set_has_more(HasMore);211 logResponse(LastMessage);212 Reply->Write(LastMessage);213 SPAN_ATTACH(Tracer, "Sent", Sent);214 SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);215 logRequestSummary("v1/FuzzyFind", Sent, StartTime);216 return grpc::Status::OK;217 }218 219 grpc::Status Refs(grpc::ServerContext *Context, const RefsRequest *Request,220 grpc::ServerWriter<RefsReply> *Reply) override {221 auto StartTime = stopwatch::now();222 WithContextValue WithRequestContext(CurrentRequest, Context);223 logRequest(*Request);224 trace::Span Tracer("RefsRequest");225 auto Req = ProtobufMarshaller->fromProtobuf(Request);226 if (!Req) {227 elog("Can not parse RefsRequest from protobuf: {0}", Req.takeError());228 return grpc::Status::CANCELLED;229 }230 if (!Req->Limit || *Req->Limit > LimitResults) {231 log("[public] Limiting result size for Refs request from {0} to {1}.",232 Req->Limit, LimitResults);233 Req->Limit = LimitResults;234 }235 unsigned Sent = 0;236 unsigned FailedToSend = 0;237 bool HasMore = Index.refs(*Req, [&](const clangd::Ref &Item) {238 auto SerializedItem = ProtobufMarshaller->toProtobuf(Item);239 if (!SerializedItem) {240 elog("Unable to convert Ref to protobuf: {0}",241 SerializedItem.takeError());242 ++FailedToSend;243 return;244 }245 RefsReply NextMessage;246 *NextMessage.mutable_stream_result() = *SerializedItem;247 logResponse(NextMessage);248 Reply->Write(NextMessage);249 ++Sent;250 });251 RefsReply LastMessage;252 LastMessage.mutable_final_result()->set_has_more(HasMore);253 logResponse(LastMessage);254 Reply->Write(LastMessage);255 SPAN_ATTACH(Tracer, "Sent", Sent);256 SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);257 logRequestSummary("v1/Refs", Sent, StartTime);258 return grpc::Status::OK;259 }260 261 grpc::Status262 ContainedRefs(grpc::ServerContext *Context,263 const ContainedRefsRequest *Request,264 grpc::ServerWriter<ContainedRefsReply> *Reply) override {265 auto StartTime = stopwatch::now();266 WithContextValue WithRequestContext(CurrentRequest, Context);267 logRequest(*Request);268 trace::Span Tracer("ContainedRefsRequest");269 auto Req = ProtobufMarshaller->fromProtobuf(Request);270 if (!Req) {271 elog("Can not parse ContainedRefsRequest from protobuf: {0}",272 Req.takeError());273 return grpc::Status::CANCELLED;274 }275 if (!Req->Limit || *Req->Limit > LimitResults) {276 log("[public] Limiting result size for ContainedRefs request from {0} to "277 "{1}.",278 Req->Limit, LimitResults);279 Req->Limit = LimitResults;280 }281 unsigned Sent = 0;282 unsigned FailedToSend = 0;283 bool HasMore =284 Index.containedRefs(*Req, [&](const clangd::ContainedRefsResult &Item) {285 auto SerializedItem = ProtobufMarshaller->toProtobuf(Item);286 if (!SerializedItem) {287 elog("Unable to convert ContainedRefsResult to protobuf: {0}",288 SerializedItem.takeError());289 ++FailedToSend;290 return;291 }292 ContainedRefsReply NextMessage;293 *NextMessage.mutable_stream_result() = *SerializedItem;294 logResponse(NextMessage);295 Reply->Write(NextMessage);296 ++Sent;297 });298 ContainedRefsReply LastMessage;299 LastMessage.mutable_final_result()->set_has_more(HasMore);300 logResponse(LastMessage);301 Reply->Write(LastMessage);302 SPAN_ATTACH(Tracer, "Sent", Sent);303 SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);304 logRequestSummary("v1/ContainedRefs", Sent, StartTime);305 return grpc::Status::OK;306 }307 308 grpc::Status Relations(grpc::ServerContext *Context,309 const RelationsRequest *Request,310 grpc::ServerWriter<RelationsReply> *Reply) override {311 auto StartTime = stopwatch::now();312 WithContextValue WithRequestContext(CurrentRequest, Context);313 logRequest(*Request);314 trace::Span Tracer("RelationsRequest");315 auto Req = ProtobufMarshaller->fromProtobuf(Request);316 if (!Req) {317 elog("Can not parse RelationsRequest from protobuf: {0}",318 Req.takeError());319 return grpc::Status::CANCELLED;320 }321 if (!Req->Limit || *Req->Limit > LimitResults) {322 log("[public] Limiting result size for Relations request from {0} to "323 "{1}.",324 Req->Limit, LimitResults);325 Req->Limit = LimitResults;326 }327 unsigned Sent = 0;328 unsigned FailedToSend = 0;329 Index.relations(330 *Req, [&](const SymbolID &Subject, const clangd::Symbol &Object) {331 auto SerializedItem = ProtobufMarshaller->toProtobuf(Subject, Object);332 if (!SerializedItem) {333 elog("Unable to convert Relation to protobuf: {0}",334 SerializedItem.takeError());335 ++FailedToSend;336 return;337 }338 RelationsReply NextMessage;339 *NextMessage.mutable_stream_result() = *SerializedItem;340 logResponse(NextMessage);341 Reply->Write(NextMessage);342 ++Sent;343 });344 RelationsReply LastMessage;345 LastMessage.mutable_final_result()->set_has_more(true);346 logResponse(LastMessage);347 Reply->Write(LastMessage);348 SPAN_ATTACH(Tracer, "Sent", Sent);349 SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);350 logRequestSummary("v1/Relations", Sent, StartTime);351 return grpc::Status::OK;352 }353 354 grpc::Status355 ReverseRelations(grpc::ServerContext *Context,356 const RelationsRequest *Request,357 grpc::ServerWriter<RelationsReply> *Reply) override {358 auto StartTime = stopwatch::now();359 WithContextValue WithRequestContext(CurrentRequest, Context);360 logRequest(*Request);361 trace::Span Tracer("ReverseRelationsRequest");362 auto Req = ProtobufMarshaller->fromProtobuf(Request);363 if (!Req) {364 elog("Can not parse ReverseRelationsRequest from protobuf: {0}",365 Req.takeError());366 return grpc::Status::CANCELLED;367 }368 if (!Req->Limit || *Req->Limit > LimitResults) {369 log("[public] Limiting result size for ReverseRelations request from {0} "370 "to "371 "{1}.",372 Req->Limit, LimitResults);373 Req->Limit = LimitResults;374 }375 unsigned Sent = 0;376 unsigned FailedToSend = 0;377 Index.reverseRelations(378 *Req, [&](const SymbolID &Subject, const clangd::Symbol &Object) {379 auto SerializedItem = ProtobufMarshaller->toProtobuf(Subject, Object);380 if (!SerializedItem) {381 elog("Unable to convert Relation to protobuf: {0}",382 SerializedItem.takeError());383 ++FailedToSend;384 return;385 }386 RelationsReply NextMessage;387 *NextMessage.mutable_stream_result() = *SerializedItem;388 logResponse(NextMessage);389 Reply->Write(NextMessage);390 ++Sent;391 });392 RelationsReply LastMessage;393 LastMessage.mutable_final_result()->set_has_more(true);394 logResponse(LastMessage);395 Reply->Write(LastMessage);396 SPAN_ATTACH(Tracer, "Sent", Sent);397 SPAN_ATTACH(Tracer, "Failed to send", FailedToSend);398 logRequestSummary("v1/ReverseRelations", Sent, StartTime);399 return grpc::Status::OK;400 }401 402 // Proxy object to allow proto messages to be lazily serialized as text.403 struct TextProto {404 const google::protobuf::Message &M;405 friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,406 const TextProto &P) {407 return OS << P.M.DebugString();408 }409 };410 411 void logRequest(const google::protobuf::Message &M) {412 vlog("<<< {0}\n{1}", M.GetDescriptor()->name(), TextProto{M});413 }414 void logResponse(const google::protobuf::Message &M) {415 vlog(">>> {0}\n{1}", M.GetDescriptor()->name(), TextProto{M});416 }417 void logRequestSummary(llvm::StringLiteral RequestName, unsigned Sent,418 stopwatch::time_point StartTime) {419 auto Duration = stopwatch::now() - StartTime;420 auto Millis =421 std::chrono::duration_cast<std::chrono::milliseconds>(Duration).count();422 log("[public] request {0} => OK: {1} results in {2}ms", RequestName, Sent,423 Millis);424 }425 426 std::unique_ptr<Marshaller> ProtobufMarshaller;427 clangd::SymbolIndex &Index;428};429 430class Monitor final : public v1::Monitor::Service {431public:432 Monitor(llvm::sys::TimePoint<> IndexAge)433 : StartTime(std::chrono::system_clock::now()), IndexBuildTime(IndexAge) {}434 435 void updateIndex(llvm::sys::TimePoint<> UpdateTime) {436 IndexBuildTime.exchange(UpdateTime);437 }438 439private:440 // FIXME(kirillbobyrev): Most fields should be populated when the index441 // reloads (probably in adjacent metadata.txt file next to loaded .idx) but442 // they aren't right now.443 grpc::Status MonitoringInfo(grpc::ServerContext *Context,444 const v1::MonitoringInfoRequest *Request,445 v1::MonitoringInfoReply *Reply) override {446 Reply->set_uptime_seconds(std::chrono::duration_cast<std::chrono::seconds>(447 std::chrono::system_clock::now() - StartTime)448 .count());449 // FIXME(kirillbobyrev): We are currently making use of the last450 // modification time of the index artifact to deduce its age. This is wrong451 // as it doesn't account for the indexing delay. Propagate some metadata452 // with the index artifacts to indicate time of the commit we indexed.453 Reply->set_index_age_seconds(454 std::chrono::duration_cast<std::chrono::seconds>(455 std::chrono::system_clock::now() - IndexBuildTime.load())456 .count());457 return grpc::Status::OK;458 }459 460 const llvm::sys::TimePoint<> StartTime;461 std::atomic<llvm::sys::TimePoint<>> IndexBuildTime;462};463 464void maybeTrimMemory() {465#if defined(__GLIBC__) && CLANGD_MALLOC_TRIM466 malloc_trim(0);467#endif468}469 470// Detect changes in \p IndexPath file and load new versions of the index471// whenever they become available.472void hotReload(clangd::SwapIndex &Index, llvm::StringRef IndexPath,473 llvm::vfs::Status &LastStatus,474 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &FS,475 Monitor &Monitor) {476 // glibc malloc doesn't shrink an arena if there are items living at the end,477 // which might happen since we destroy the old index after building new one.478 // Trim more aggresively to keep memory usage of the server low.479 // Note that we do it deliberately here rather than after Index.reset(),480 // because old index might still be kept alive after the reset call if we are481 // serving requests.482 maybeTrimMemory();483 auto Status = FS->status(IndexPath);484 // Requested file is same as loaded index: no reload is needed.485 if (!Status || (Status->getLastModificationTime() ==486 LastStatus.getLastModificationTime() &&487 Status->getSize() == LastStatus.getSize()))488 return;489 vlog("Found different index version: existing index was modified at "490 "{0}, new index was modified at {1}. Attempting to reload.",491 LastStatus.getLastModificationTime(), Status->getLastModificationTime());492 LastStatus = *Status;493 std::unique_ptr<clang::clangd::SymbolIndex> NewIndex =494 loadIndex(IndexPath, SymbolOrigin::Static, /*UseDex=*/true,495 /*SupportContainedRefs=*/true);496 if (!NewIndex) {497 elog("Failed to load new index. Old index will be served.");498 return;499 }500 Index.reset(std::move(NewIndex));501 Monitor.updateIndex(Status->getLastModificationTime());502 log("New index version loaded. Last modification time: {0}, size: {1} bytes.",503 Status->getLastModificationTime(), Status->getSize());504}505 506void runServerAndWait(clangd::SymbolIndex &Index, llvm::StringRef ServerAddress,507 llvm::StringRef IndexPath, Monitor &Monitor) {508 RemoteIndexServer Service(Index, IndexRoot);509 510 grpc::EnableDefaultHealthCheckService(true);511#if ENABLE_GRPC_REFLECTION512 grpc::reflection::InitProtoReflectionServerBuilderPlugin();513#endif514 grpc::ServerBuilder Builder;515 Builder.AddListeningPort(ServerAddress.str(),516 grpc::InsecureServerCredentials());517 Builder.AddChannelArgument(GRPC_ARG_MAX_CONNECTION_IDLE_MS,518 IdleTimeoutSeconds * 1000);519 Builder.RegisterService(&Service);520 Builder.RegisterService(&Monitor);521 std::unique_ptr<grpc::Server> Server(Builder.BuildAndStart());522 log("Server listening on {0}", ServerAddress);523 524 std::thread ServerShutdownWatcher([&]() {525 static constexpr auto WatcherFrequency = std::chrono::seconds(5);526 while (!clang::clangd::shutdownRequested())527 std::this_thread::sleep_for(WatcherFrequency);528 Server->Shutdown();529 });530 531 Server->Wait();532 ServerShutdownWatcher.join();533}534 535std::unique_ptr<Logger> makeLogger(llvm::StringRef LogPrefix,536 llvm::raw_ostream &OS) {537 std::unique_ptr<Logger> Base;538 if (LogPublic) {539 // Redacted mode:540 // - messages outside the scope of a request: log fully541 // - messages tagged [public]: log fully542 // - errors: log the format string543 // - others: drop544 class RedactedLogger : public StreamLogger {545 public:546 using StreamLogger::StreamLogger;547 void log(Level L, const char *Fmt,548 const llvm::formatv_object_base &Message) override {549 if (Context::current().get(CurrentRequest) == nullptr ||550 llvm::StringRef(Fmt).starts_with("[public]"))551 return StreamLogger::log(L, Fmt, Message);552 if (L >= Error)553 return StreamLogger::log(L, Fmt,554 llvm::formatv("[redacted] {0}", Fmt));555 }556 };557 Base = std::make_unique<RedactedLogger>(OS, LogLevel);558 } else {559 Base = std::make_unique<StreamLogger>(OS, LogLevel);560 }561 562 if (LogPrefix.empty())563 return Base;564 class PrefixedLogger : public Logger {565 std::string LogPrefix;566 std::unique_ptr<Logger> Base;567 568 public:569 PrefixedLogger(llvm::StringRef LogPrefix, std::unique_ptr<Logger> Base)570 : LogPrefix(LogPrefix.str()), Base(std::move(Base)) {}571 void log(Level L, const char *Fmt,572 const llvm::formatv_object_base &Message) override {573 Base->log(L, Fmt, llvm::formatv("[{0}] {1}", LogPrefix, Message));574 }575 };576 return std::make_unique<PrefixedLogger>(LogPrefix, std::move(Base));577}578 579} // namespace580} // namespace remote581} // namespace clangd582} // namespace clang583 584using clang::clangd::elog;585 586int main(int argc, char *argv[]) {587 using namespace clang::clangd::remote;588 llvm::cl::ParseCommandLineOptions(argc, argv, Overview);589 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);590 llvm::sys::SetInterruptFunction(&clang::clangd::requestShutdown);591 592 if (!llvm::sys::path::is_absolute(IndexRoot)) {593 llvm::errs() << "Index root should be an absolute path.\n";594 return -1;595 }596 597 llvm::errs().SetBuffered();598 auto Logger = makeLogger(LogPrefix.getValue(), llvm::errs());599 clang::clangd::LoggingSession LoggingSession(*Logger);600 601 std::optional<llvm::raw_fd_ostream> TracerStream;602 std::unique_ptr<clang::clangd::trace::EventTracer> Tracer;603 if (!TraceFile.empty()) {604 std::error_code EC;605 TracerStream.emplace(TraceFile, EC,606 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);607 if (EC) {608 TracerStream.reset();609 elog("Error while opening trace file {0}: {1}", TraceFile, EC.message());610 } else {611 // FIXME(kirillbobyrev): Also create metrics tracer to track latency and612 // accumulate other request statistics.613 Tracer = clang::clangd::trace::createJSONTracer(*TracerStream,614 /*PrettyPrint=*/false);615 clang::clangd::vlog("Successfully created a tracer.");616 }617 }618 619 std::optional<clang::clangd::trace::Session> TracingSession;620 if (Tracer)621 TracingSession.emplace(*Tracer);622 623 clang::clangd::RealThreadsafeFS TFS;624 auto FS = TFS.view(std::nullopt);625 auto Status = FS->status(IndexPath);626 if (!Status) {627 elog("{0} does not exist.", IndexPath);628 return Status.getError().value();629 }630 631 auto SymIndex = clang::clangd::loadIndex(632 IndexPath, clang::clangd::SymbolOrigin::Static, /*UseDex=*/true,633 /*SupportContainedRefs=*/true);634 if (!SymIndex) {635 llvm::errs() << "Failed to open the index.\n";636 return -1;637 }638 clang::clangd::SwapIndex Index(std::move(SymIndex));639 640 Monitor Monitor(Status->getLastModificationTime());641 642 std::thread HotReloadThread([&Index, &Status, &FS, &Monitor]() {643 llvm::vfs::Status LastStatus = *Status;644 static constexpr auto RefreshFrequency = std::chrono::seconds(30);645 while (!clang::clangd::shutdownRequested()) {646 hotReload(Index, llvm::StringRef(IndexPath), LastStatus, FS, Monitor);647 std::this_thread::sleep_for(RefreshFrequency);648 }649 });650 651 runServerAndWait(Index, ServerAddress, IndexPath, Monitor);652 653 HotReloadThread.join();654}655