brintos

brintos / llvm-project-archived public Read only

0
0
Text · 23.7 KiB · af9e9c3 Raw
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