1 //===--- Client.cpp ----------------------------------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include <grpc++/grpc++.h>
13 #include "Service.grpc.pb.h"
14 #include "index/Index.h"
15 #include "marshalling/Marshalling.h"
16 #include "support/Logger.h"
17 #include "support/Trace.h"
18 #include "llvm/ADT/SmallString.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/Error.h"
31 llvm::StringRef
toString(const grpc_connectivity_state
&State
) {
33 case GRPC_CHANNEL_IDLE
:
35 case GRPC_CHANNEL_CONNECTING
:
37 case GRPC_CHANNEL_READY
:
39 case GRPC_CHANNEL_TRANSIENT_FAILURE
:
40 return "transient failure";
41 case GRPC_CHANNEL_SHUTDOWN
:
44 llvm_unreachable("Not a valid grpc_connectivity_state.");
47 class IndexClient
: public clangd::SymbolIndex
{
48 void updateConnectionStatus() const {
49 auto NewStatus
= Channel
->GetState(/*try_to_connect=*/false);
50 auto OldStatus
= ConnectionStatus
.exchange(NewStatus
);
51 if (OldStatus
!= NewStatus
)
52 vlog("Remote index connection [{0}]: {1} => {2}", ServerAddress
,
53 toString(OldStatus
), toString(NewStatus
));
56 template <typename RequestT
, typename ReplyT
>
57 using StreamingCall
= std::unique_ptr
<grpc::ClientReader
<ReplyT
>> (
58 remote::v1::SymbolIndex::Stub::*)(grpc::ClientContext
*,
61 template <typename RequestT
, typename ReplyT
, typename ClangdRequestT
,
63 bool streamRPC(ClangdRequestT Request
,
64 StreamingCall
<RequestT
, ReplyT
> RPCCall
,
65 CallbackT Callback
) const {
66 updateConnectionStatus();
67 // We initialize to true because stream might be broken before we see the
68 // final message. In such a case there are actually more results on the
69 // stream, but we couldn't get to them.
71 trace::Span
Tracer(RequestT::descriptor()->name());
72 const auto RPCRequest
= ProtobufMarshaller
->toProtobuf(Request
);
73 SPAN_ATTACH(Tracer
, "Request", RPCRequest
.DebugString());
74 grpc::ClientContext Context
;
75 Context
.AddMetadata("version", versionString());
76 Context
.AddMetadata("features", featureString());
77 Context
.AddMetadata("platform", platformString());
78 std::chrono::system_clock::time_point StartTime
=
79 std::chrono::system_clock::now();
80 auto Deadline
= StartTime
+ DeadlineWaitingTime
;
81 Context
.set_deadline(Deadline
);
82 auto Reader
= (Stub
.get()->*RPCCall
)(&Context
, RPCRequest
);
83 dlog("Sending {0}: {1}", RequestT::descriptor()->name(),
84 RPCRequest
.DebugString());
86 unsigned Successful
= 0;
87 unsigned FailedToParse
= 0;
88 while (Reader
->Read(&Reply
)) {
89 if (!Reply
.has_stream_result()) {
90 HasMore
= Reply
.final_result().has_more();
93 auto Response
= ProtobufMarshaller
->fromProtobuf(Reply
.stream_result());
95 elog("Received invalid {0}: {1}. Reason: {2}",
96 ReplyT::descriptor()->name(), Reply
.stream_result().DebugString(),
97 Response
.takeError());
104 auto Millis
= std::chrono::duration_cast
<std::chrono::milliseconds
>(
105 std::chrono::system_clock::now() - StartTime
)
107 vlog("Remote index [{0}]: {1} => {2} results in {3}ms.", ServerAddress
,
108 RequestT::descriptor()->name(), Successful
, Millis
);
109 SPAN_ATTACH(Tracer
, "Status", Reader
->Finish().ok());
110 SPAN_ATTACH(Tracer
, "Successful", Successful
);
111 SPAN_ATTACH(Tracer
, "Failed to parse", FailedToParse
);
112 updateConnectionStatus();
118 std::shared_ptr
<grpc::Channel
> Channel
, llvm::StringRef Address
,
119 llvm::StringRef ProjectRoot
,
120 std::chrono::milliseconds DeadlineTime
= std::chrono::milliseconds(1000))
121 : Stub(remote::v1::SymbolIndex::NewStub(Channel
)), Channel(Channel
),
122 ServerAddress(Address
),
123 ConnectionStatus(Channel
->GetState(/*try_to_connect=*/true)),
124 ProtobufMarshaller(new Marshaller(/*RemoteIndexRoot=*/"",
125 /*LocalIndexRoot=*/ProjectRoot
)),
126 DeadlineWaitingTime(DeadlineTime
) {
127 assert(!ProjectRoot
.empty());
130 void lookup(const clangd::LookupRequest
&Request
,
131 llvm::function_ref
<void(const clangd::Symbol
&)> Callback
)
133 streamRPC(Request
, &remote::v1::SymbolIndex::Stub::Lookup
, Callback
);
136 bool fuzzyFind(const clangd::FuzzyFindRequest
&Request
,
137 llvm::function_ref
<void(const clangd::Symbol
&)> Callback
)
139 return streamRPC(Request
, &remote::v1::SymbolIndex::Stub::FuzzyFind
,
144 refs(const clangd::RefsRequest
&Request
,
145 llvm::function_ref
<void(const clangd::Ref
&)> Callback
) const override
{
146 return streamRPC(Request
, &remote::v1::SymbolIndex::Stub::Refs
, Callback
);
150 relations(const clangd::RelationsRequest
&Request
,
151 llvm::function_ref
<void(const SymbolID
&, const clangd::Symbol
&)>
152 Callback
) const override
{
153 streamRPC(Request
, &remote::v1::SymbolIndex::Stub::Relations
,
154 // Unpack protobuf Relation.
155 [&](std::pair
<SymbolID
, clangd::Symbol
> SubjectAndObject
) {
156 Callback(SubjectAndObject
.first
, SubjectAndObject
.second
);
160 llvm::unique_function
<IndexContents(llvm::StringRef
) const>
161 indexedFiles() const override
{
162 // FIXME: For now we always return IndexContents::None regardless of whether
163 // the file was indexed or not. A possible implementation could be
164 // based on the idea that we do not want to send a request at every
165 // call of a function returned by IndexClient::indexedFiles().
166 return [](llvm::StringRef
) { return IndexContents::None
; };
169 // IndexClient does not take any space since the data is stored on the
171 size_t estimateMemoryUsage() const override
{ return 0; }
174 std::unique_ptr
<remote::v1::SymbolIndex::Stub
> Stub
;
175 std::shared_ptr
<grpc::Channel
> Channel
;
176 llvm::SmallString
<256> ServerAddress
;
177 mutable std::atomic
<grpc_connectivity_state
> ConnectionStatus
;
178 std::unique_ptr
<Marshaller
> ProtobufMarshaller
;
179 // Each request will be terminated if it takes too long.
180 std::chrono::milliseconds DeadlineWaitingTime
;
185 std::unique_ptr
<clangd::SymbolIndex
> getClient(llvm::StringRef Address
,
186 llvm::StringRef ProjectRoot
) {
188 grpc::CreateChannel(Address
.str(), grpc::InsecureChannelCredentials());
189 return std::unique_ptr
<clangd::SymbolIndex
>(
190 new IndexClient(Channel
, Address
, ProjectRoot
));
193 } // namespace remote
194 } // namespace clangd