• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- Client.cpp ----------------------------------------------*- C++-*-===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <grpc++/grpc++.h>
10 
11 #include "Client.h"
12 #include "Service.grpc.pb.h"
13 #include "index/Index.h"
14 #include "marshalling/Marshalling.h"
15 #include "support/Logger.h"
16 #include "support/Trace.h"
17 #include "clang/Basic/Version.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Error.h"
20 
21 #include <chrono>
22 
23 namespace clang {
24 namespace clangd {
25 namespace remote {
26 namespace {
27 
28 class IndexClient : public clangd::SymbolIndex {
29   template <typename RequestT, typename ReplyT>
30   using StreamingCall = std::unique_ptr<grpc::ClientReader<ReplyT>> (
31       remote::v1::SymbolIndex::Stub::*)(grpc::ClientContext *,
32                                         const RequestT &);
33 
34   template <typename RequestT, typename ReplyT, typename ClangdRequestT,
35             typename CallbackT>
streamRPC(ClangdRequestT Request,StreamingCall<RequestT,ReplyT> RPCCall,CallbackT Callback) const36   bool streamRPC(ClangdRequestT Request,
37                  StreamingCall<RequestT, ReplyT> RPCCall,
38                  CallbackT Callback) const {
39     bool FinalResult = false;
40     trace::Span Tracer(RequestT::descriptor()->name());
41     const auto RPCRequest = ProtobufMarshaller->toProtobuf(Request);
42     SPAN_ATTACH(Tracer, "Request", RPCRequest.DebugString());
43     grpc::ClientContext Context;
44     Context.AddMetadata("version", clang::getClangToolFullVersion("clangd"));
45     std::chrono::system_clock::time_point Deadline =
46         std::chrono::system_clock::now() + DeadlineWaitingTime;
47     Context.set_deadline(Deadline);
48     auto Reader = (Stub.get()->*RPCCall)(&Context, RPCRequest);
49     ReplyT Reply;
50     unsigned Successful = 0;
51     unsigned FailedToParse = 0;
52     while (Reader->Read(&Reply)) {
53       if (!Reply.has_stream_result()) {
54         FinalResult = Reply.final_result().has_more();
55         continue;
56       }
57       auto Response = ProtobufMarshaller->fromProtobuf(Reply.stream_result());
58       if (!Response) {
59         elog("Received invalid {0}: {1}. Reason: {2}",
60              ReplyT::descriptor()->name(), Reply.stream_result().DebugString(),
61              Response.takeError());
62         ++FailedToParse;
63         continue;
64       }
65       Callback(*Response);
66       ++Successful;
67     }
68     SPAN_ATTACH(Tracer, "Status", Reader->Finish().ok());
69     SPAN_ATTACH(Tracer, "Successful", Successful);
70     SPAN_ATTACH(Tracer, "Failed to parse", FailedToParse);
71     return FinalResult;
72   }
73 
74 public:
IndexClient(std::shared_ptr<grpc::Channel> Channel,llvm::StringRef ProjectRoot,std::chrono::milliseconds DeadlineTime=std::chrono::milliseconds (1000))75   IndexClient(
76       std::shared_ptr<grpc::Channel> Channel, llvm::StringRef ProjectRoot,
77       std::chrono::milliseconds DeadlineTime = std::chrono::milliseconds(1000))
78       : Stub(remote::v1::SymbolIndex::NewStub(Channel)),
79         ProtobufMarshaller(new Marshaller(/*RemoteIndexRoot=*/"",
80                                           /*LocalIndexRoot=*/ProjectRoot)),
81         DeadlineWaitingTime(DeadlineTime) {
82     assert(!ProjectRoot.empty());
83   }
84 
lookup(const clangd::LookupRequest & Request,llvm::function_ref<void (const clangd::Symbol &)> Callback) const85   void lookup(const clangd::LookupRequest &Request,
86               llvm::function_ref<void(const clangd::Symbol &)> Callback)
87       const override {
88     streamRPC(Request, &remote::v1::SymbolIndex::Stub::Lookup, Callback);
89   }
90 
fuzzyFind(const clangd::FuzzyFindRequest & Request,llvm::function_ref<void (const clangd::Symbol &)> Callback) const91   bool fuzzyFind(const clangd::FuzzyFindRequest &Request,
92                  llvm::function_ref<void(const clangd::Symbol &)> Callback)
93       const override {
94     return streamRPC(Request, &remote::v1::SymbolIndex::Stub::FuzzyFind,
95                      Callback);
96   }
97 
98   bool
refs(const clangd::RefsRequest & Request,llvm::function_ref<void (const clangd::Ref &)> Callback) const99   refs(const clangd::RefsRequest &Request,
100        llvm::function_ref<void(const clangd::Ref &)> Callback) const override {
101     return streamRPC(Request, &remote::v1::SymbolIndex::Stub::Refs, Callback);
102   }
103 
104   void
relations(const clangd::RelationsRequest & Request,llvm::function_ref<void (const SymbolID &,const clangd::Symbol &)> Callback) const105   relations(const clangd::RelationsRequest &Request,
106             llvm::function_ref<void(const SymbolID &, const clangd::Symbol &)>
107                 Callback) const override {
108     streamRPC(Request, &remote::v1::SymbolIndex::Stub::Relations,
109               // Unpack protobuf Relation.
110               [&](std::pair<SymbolID, clangd::Symbol> SubjectAndObject) {
111                 Callback(SubjectAndObject.first, SubjectAndObject.second);
112               });
113   }
114 
115   // IndexClient does not take any space since the data is stored on the
116   // server.
estimateMemoryUsage() const117   size_t estimateMemoryUsage() const override { return 0; }
118 
119 private:
120   std::unique_ptr<remote::v1::SymbolIndex::Stub> Stub;
121   std::unique_ptr<Marshaller> ProtobufMarshaller;
122   // Each request will be terminated if it takes too long.
123   std::chrono::milliseconds DeadlineWaitingTime;
124 };
125 
126 } // namespace
127 
getClient(llvm::StringRef Address,llvm::StringRef ProjectRoot)128 std::unique_ptr<clangd::SymbolIndex> getClient(llvm::StringRef Address,
129                                                llvm::StringRef ProjectRoot) {
130   const auto Channel =
131       grpc::CreateChannel(Address.str(), grpc::InsecureChannelCredentials());
132   Channel->GetState(true);
133   return std::unique_ptr<clangd::SymbolIndex>(
134       new IndexClient(Channel, ProjectRoot));
135 }
136 
137 } // namespace remote
138 } // namespace clangd
139 } // namespace clang
140