• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2017 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #include <grpc/impl/codegen/port_platform.h>
20 
21 #include "src/core/lib/channel/channelz.h"
22 
23 #include "absl/strings/strip.h"
24 
25 #include <grpc/grpc.h>
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28 #include <grpc/support/string_util.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "src/core/lib/channel/channelz_registry.h"
34 #include "src/core/lib/channel/status_util.h"
35 #include "src/core/lib/gpr/string.h"
36 #include "src/core/lib/gpr/useful.h"
37 #include "src/core/lib/gprpp/atomic.h"
38 #include "src/core/lib/gprpp/host_port.h"
39 #include "src/core/lib/gprpp/memory.h"
40 #include "src/core/lib/iomgr/error.h"
41 #include "src/core/lib/iomgr/exec_ctx.h"
42 #include "src/core/lib/slice/b64.h"
43 #include "src/core/lib/slice/slice_internal.h"
44 #include "src/core/lib/surface/channel.h"
45 #include "src/core/lib/surface/server.h"
46 #include "src/core/lib/transport/connectivity_state.h"
47 #include "src/core/lib/transport/error_utils.h"
48 #include "src/core/lib/uri/uri_parser.h"
49 
50 namespace grpc_core {
51 namespace channelz {
52 
53 //
54 // BaseNode
55 //
56 
BaseNode(EntityType type,std::string name)57 BaseNode::BaseNode(EntityType type, std::string name)
58     : type_(type), uuid_(-1), name_(std::move(name)) {
59   // The registry will set uuid_ under its lock.
60   ChannelzRegistry::Register(this);
61 }
62 
~BaseNode()63 BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
64 
RenderJsonString()65 std::string BaseNode::RenderJsonString() {
66   Json json = RenderJson();
67   return json.Dump();
68 }
69 
70 //
71 // CallCountingHelper
72 //
73 
CallCountingHelper()74 CallCountingHelper::CallCountingHelper() {
75   num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
76   per_cpu_counter_data_storage_.reserve(num_cores_);
77   for (size_t i = 0; i < num_cores_; ++i) {
78     per_cpu_counter_data_storage_.emplace_back();
79   }
80 }
81 
RecordCallStarted()82 void CallCountingHelper::RecordCallStarted() {
83   AtomicCounterData& data =
84       per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()];
85   data.calls_started.FetchAdd(1, MemoryOrder::RELAXED);
86   data.last_call_started_cycle.Store(gpr_get_cycle_counter(),
87                                      MemoryOrder::RELAXED);
88 }
89 
RecordCallFailed()90 void CallCountingHelper::RecordCallFailed() {
91   per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
92       .calls_failed.FetchAdd(1, MemoryOrder::RELAXED);
93 }
94 
RecordCallSucceeded()95 void CallCountingHelper::RecordCallSucceeded() {
96   per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
97       .calls_succeeded.FetchAdd(1, MemoryOrder::RELAXED);
98 }
99 
CollectData(CounterData * out)100 void CallCountingHelper::CollectData(CounterData* out) {
101   for (size_t core = 0; core < num_cores_; ++core) {
102     AtomicCounterData& data = per_cpu_counter_data_storage_[core];
103 
104     out->calls_started += data.calls_started.Load(MemoryOrder::RELAXED);
105     out->calls_succeeded +=
106         per_cpu_counter_data_storage_[core].calls_succeeded.Load(
107             MemoryOrder::RELAXED);
108     out->calls_failed += per_cpu_counter_data_storage_[core].calls_failed.Load(
109         MemoryOrder::RELAXED);
110     const gpr_cycle_counter last_call =
111         per_cpu_counter_data_storage_[core].last_call_started_cycle.Load(
112             MemoryOrder::RELAXED);
113     if (last_call > out->last_call_started_cycle) {
114       out->last_call_started_cycle = last_call;
115     }
116   }
117 }
118 
PopulateCallCounts(Json::Object * json)119 void CallCountingHelper::PopulateCallCounts(Json::Object* json) {
120   CounterData data;
121   CollectData(&data);
122   if (data.calls_started != 0) {
123     (*json)["callsStarted"] = std::to_string(data.calls_started);
124     gpr_timespec ts = gpr_convert_clock_type(
125         gpr_cycle_counter_to_time(data.last_call_started_cycle),
126         GPR_CLOCK_REALTIME);
127     (*json)["lastCallStartedTimestamp"] = gpr_format_timespec(ts);
128   }
129   if (data.calls_succeeded != 0) {
130     (*json)["callsSucceeded"] = std::to_string(data.calls_succeeded);
131   }
132   if (data.calls_failed) {
133     (*json)["callsFailed"] = std::to_string(data.calls_failed);
134   }
135 }
136 
137 //
138 // ChannelNode
139 //
140 
ChannelNode(std::string target,size_t channel_tracer_max_nodes,bool is_internal_channel)141 ChannelNode::ChannelNode(std::string target, size_t channel_tracer_max_nodes,
142                          bool is_internal_channel)
143     : BaseNode(is_internal_channel ? EntityType::kInternalChannel
144                                    : EntityType::kTopLevelChannel,
145                target),
146       target_(std::move(target)),
147       trace_(channel_tracer_max_nodes) {}
148 
GetChannelConnectivityStateChangeString(grpc_connectivity_state state)149 const char* ChannelNode::GetChannelConnectivityStateChangeString(
150     grpc_connectivity_state state) {
151   switch (state) {
152     case GRPC_CHANNEL_IDLE:
153       return "Channel state change to IDLE";
154     case GRPC_CHANNEL_CONNECTING:
155       return "Channel state change to CONNECTING";
156     case GRPC_CHANNEL_READY:
157       return "Channel state change to READY";
158     case GRPC_CHANNEL_TRANSIENT_FAILURE:
159       return "Channel state change to TRANSIENT_FAILURE";
160     case GRPC_CHANNEL_SHUTDOWN:
161       return "Channel state change to SHUTDOWN";
162   }
163   GPR_UNREACHABLE_CODE(return "UNKNOWN");
164 }
165 
RenderJson()166 Json ChannelNode::RenderJson() {
167   Json::Object data = {
168       {"target", target_},
169   };
170   // Connectivity state.
171   // If low-order bit is on, then the field is set.
172   int state_field = connectivity_state_.Load(MemoryOrder::RELAXED);
173   if ((state_field & 1) != 0) {
174     grpc_connectivity_state state =
175         static_cast<grpc_connectivity_state>(state_field >> 1);
176     data["state"] = Json::Object{
177         {"state", ConnectivityStateName(state)},
178     };
179   }
180   // Fill in the channel trace if applicable.
181   Json trace_json = trace_.RenderJson();
182   if (trace_json.type() != Json::Type::JSON_NULL) {
183     data["trace"] = std::move(trace_json);
184   }
185   // Ask CallCountingHelper to populate call count data.
186   call_counter_.PopulateCallCounts(&data);
187   // Construct outer object.
188   Json::Object json = {
189       {"ref",
190        Json::Object{
191            {"channelId", std::to_string(uuid())},
192        }},
193       {"data", std::move(data)},
194   };
195   // Template method. Child classes may override this to add their specific
196   // functionality.
197   PopulateChildRefs(&json);
198   return json;
199 }
200 
PopulateChildRefs(Json::Object * json)201 void ChannelNode::PopulateChildRefs(Json::Object* json) {
202   MutexLock lock(&child_mu_);
203   if (!child_subchannels_.empty()) {
204     Json::Array array;
205     for (intptr_t subchannel_uuid : child_subchannels_) {
206       array.emplace_back(Json::Object{
207           {"subchannelId", std::to_string(subchannel_uuid)},
208       });
209     }
210     (*json)["subchannelRef"] = std::move(array);
211   }
212   if (!child_channels_.empty()) {
213     Json::Array array;
214     for (intptr_t channel_uuid : child_channels_) {
215       array.emplace_back(Json::Object{
216           {"channelId", std::to_string(channel_uuid)},
217       });
218     }
219     (*json)["channelRef"] = std::move(array);
220   }
221 }
222 
SetConnectivityState(grpc_connectivity_state state)223 void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
224   // Store with low-order bit set to indicate that the field is set.
225   int state_field = (state << 1) + 1;
226   connectivity_state_.Store(state_field, MemoryOrder::RELAXED);
227 }
228 
AddChildChannel(intptr_t child_uuid)229 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
230   MutexLock lock(&child_mu_);
231   child_channels_.insert(child_uuid);
232 }
233 
RemoveChildChannel(intptr_t child_uuid)234 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
235   MutexLock lock(&child_mu_);
236   child_channels_.erase(child_uuid);
237 }
238 
AddChildSubchannel(intptr_t child_uuid)239 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
240   MutexLock lock(&child_mu_);
241   child_subchannels_.insert(child_uuid);
242 }
243 
RemoveChildSubchannel(intptr_t child_uuid)244 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
245   MutexLock lock(&child_mu_);
246   child_subchannels_.erase(child_uuid);
247 }
248 
249 //
250 // ServerNode
251 //
252 
ServerNode(size_t channel_tracer_max_nodes)253 ServerNode::ServerNode(size_t channel_tracer_max_nodes)
254     : BaseNode(EntityType::kServer, ""), trace_(channel_tracer_max_nodes) {}
255 
~ServerNode()256 ServerNode::~ServerNode() {}
257 
AddChildSocket(RefCountedPtr<SocketNode> node)258 void ServerNode::AddChildSocket(RefCountedPtr<SocketNode> node) {
259   MutexLock lock(&child_mu_);
260   child_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
261 }
262 
RemoveChildSocket(intptr_t child_uuid)263 void ServerNode::RemoveChildSocket(intptr_t child_uuid) {
264   MutexLock lock(&child_mu_);
265   child_sockets_.erase(child_uuid);
266 }
267 
AddChildListenSocket(RefCountedPtr<ListenSocketNode> node)268 void ServerNode::AddChildListenSocket(RefCountedPtr<ListenSocketNode> node) {
269   MutexLock lock(&child_mu_);
270   child_listen_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
271 }
272 
RemoveChildListenSocket(intptr_t child_uuid)273 void ServerNode::RemoveChildListenSocket(intptr_t child_uuid) {
274   MutexLock lock(&child_mu_);
275   child_listen_sockets_.erase(child_uuid);
276 }
277 
RenderServerSockets(intptr_t start_socket_id,intptr_t max_results)278 std::string ServerNode::RenderServerSockets(intptr_t start_socket_id,
279                                             intptr_t max_results) {
280   GPR_ASSERT(start_socket_id >= 0);
281   GPR_ASSERT(max_results >= 0);
282   // If user does not set max_results, we choose 500.
283   size_t pagination_limit = max_results == 0 ? 500 : max_results;
284   Json::Object object;
285   {
286     MutexLock lock(&child_mu_);
287     size_t sockets_rendered = 0;
288     // Create list of socket refs.
289     Json::Array array;
290     auto it = child_sockets_.lower_bound(start_socket_id);
291     for (; it != child_sockets_.end() && sockets_rendered < pagination_limit;
292          ++it, ++sockets_rendered) {
293       array.emplace_back(Json::Object{
294           {"socketId", std::to_string(it->first)},
295           {"name", it->second->name()},
296       });
297     }
298     object["socketRef"] = std::move(array);
299     if (it == child_sockets_.end()) object["end"] = true;
300   }
301   Json json = std::move(object);
302   return json.Dump();
303 }
304 
RenderJson()305 Json ServerNode::RenderJson() {
306   Json::Object data;
307   // Fill in the channel trace if applicable.
308   Json trace_json = trace_.RenderJson();
309   if (trace_json.type() != Json::Type::JSON_NULL) {
310     data["trace"] = std::move(trace_json);
311   }
312   // Ask CallCountingHelper to populate call count data.
313   call_counter_.PopulateCallCounts(&data);
314   // Construct top-level object.
315   Json::Object object = {
316       {"ref",
317        Json::Object{
318            {"serverId", std::to_string(uuid())},
319        }},
320       {"data", std::move(data)},
321   };
322   // Render listen sockets.
323   {
324     MutexLock lock(&child_mu_);
325     if (!child_listen_sockets_.empty()) {
326       Json::Array array;
327       for (const auto& it : child_listen_sockets_) {
328         array.emplace_back(Json::Object{
329             {"socketId", std::to_string(it.first)},
330             {"name", it.second->name()},
331         });
332       }
333       object["listenSocket"] = std::move(array);
334     }
335   }
336   return object;
337 }
338 
339 //
340 // SocketNode
341 //
342 
343 namespace {
344 
PopulateSocketAddressJson(Json::Object * json,const char * name,const char * addr_str)345 void PopulateSocketAddressJson(Json::Object* json, const char* name,
346                                const char* addr_str) {
347   if (addr_str == nullptr) return;
348   Json::Object data;
349   absl::StatusOr<URI> uri = URI::Parse(addr_str);
350   if (uri.ok() && (uri->scheme() == "ipv4" || uri->scheme() == "ipv6")) {
351     std::string host;
352     std::string port;
353     GPR_ASSERT(
354         SplitHostPort(absl::StripPrefix(uri->path(), "/"), &host, &port));
355     int port_num = -1;
356     if (!port.empty()) {
357       port_num = atoi(port.data());
358     }
359     char* b64_host = grpc_base64_encode(host.data(), host.size(), false, false);
360     data["tcpip_address"] = Json::Object{
361         {"port", port_num},
362         {"ip_address", b64_host},
363     };
364     gpr_free(b64_host);
365   } else if (uri.ok() && uri->scheme() == "unix") {
366     data["uds_address"] = Json::Object{
367         {"filename", uri->path()},
368     };
369   } else {
370     data["other_address"] = Json::Object{
371         {"name", addr_str},
372     };
373   }
374   (*json)[name] = std::move(data);
375 }
376 
377 }  // namespace
378 
SocketNode(std::string local,std::string remote,std::string name)379 SocketNode::SocketNode(std::string local, std::string remote, std::string name)
380     : BaseNode(EntityType::kSocket, std::move(name)),
381       local_(std::move(local)),
382       remote_(std::move(remote)) {}
383 
RecordStreamStartedFromLocal()384 void SocketNode::RecordStreamStartedFromLocal() {
385   streams_started_.FetchAdd(1, MemoryOrder::RELAXED);
386   last_local_stream_created_cycle_.Store(gpr_get_cycle_counter(),
387                                          MemoryOrder::RELAXED);
388 }
389 
RecordStreamStartedFromRemote()390 void SocketNode::RecordStreamStartedFromRemote() {
391   streams_started_.FetchAdd(1, MemoryOrder::RELAXED);
392   last_remote_stream_created_cycle_.Store(gpr_get_cycle_counter(),
393                                           MemoryOrder::RELAXED);
394 }
395 
RecordMessagesSent(uint32_t num_sent)396 void SocketNode::RecordMessagesSent(uint32_t num_sent) {
397   messages_sent_.FetchAdd(num_sent, MemoryOrder::RELAXED);
398   last_message_sent_cycle_.Store(gpr_get_cycle_counter(), MemoryOrder::RELAXED);
399 }
400 
RecordMessageReceived()401 void SocketNode::RecordMessageReceived() {
402   messages_received_.FetchAdd(1, MemoryOrder::RELAXED);
403   last_message_received_cycle_.Store(gpr_get_cycle_counter(),
404                                      MemoryOrder::RELAXED);
405 }
406 
RenderJson()407 Json SocketNode::RenderJson() {
408   // Create and fill the data child.
409   Json::Object data;
410   gpr_timespec ts;
411   int64_t streams_started = streams_started_.Load(MemoryOrder::RELAXED);
412   if (streams_started != 0) {
413     data["streamsStarted"] = std::to_string(streams_started);
414     gpr_cycle_counter last_local_stream_created_cycle =
415         last_local_stream_created_cycle_.Load(MemoryOrder::RELAXED);
416     if (last_local_stream_created_cycle != 0) {
417       ts = gpr_convert_clock_type(
418           gpr_cycle_counter_to_time(last_local_stream_created_cycle),
419           GPR_CLOCK_REALTIME);
420       data["lastLocalStreamCreatedTimestamp"] = gpr_format_timespec(ts);
421     }
422     gpr_cycle_counter last_remote_stream_created_cycle =
423         last_remote_stream_created_cycle_.Load(MemoryOrder::RELAXED);
424     if (last_remote_stream_created_cycle != 0) {
425       ts = gpr_convert_clock_type(
426           gpr_cycle_counter_to_time(last_remote_stream_created_cycle),
427           GPR_CLOCK_REALTIME);
428       data["lastRemoteStreamCreatedTimestamp"] = gpr_format_timespec(ts);
429     }
430   }
431   int64_t streams_succeeded = streams_succeeded_.Load(MemoryOrder::RELAXED);
432   if (streams_succeeded != 0) {
433     data["streamsSucceeded"] = std::to_string(streams_succeeded);
434   }
435   int64_t streams_failed = streams_failed_.Load(MemoryOrder::RELAXED);
436   if (streams_failed != 0) {
437     data["streamsFailed"] = std::to_string(streams_failed);
438   }
439   int64_t messages_sent = messages_sent_.Load(MemoryOrder::RELAXED);
440   if (messages_sent != 0) {
441     data["messagesSent"] = std::to_string(messages_sent);
442     ts = gpr_convert_clock_type(
443         gpr_cycle_counter_to_time(
444             last_message_sent_cycle_.Load(MemoryOrder::RELAXED)),
445         GPR_CLOCK_REALTIME);
446     data["lastMessageSentTimestamp"] = gpr_format_timespec(ts);
447   }
448   int64_t messages_received = messages_received_.Load(MemoryOrder::RELAXED);
449   if (messages_received != 0) {
450     data["messagesReceived"] = std::to_string(messages_received);
451     ts = gpr_convert_clock_type(
452         gpr_cycle_counter_to_time(
453             last_message_received_cycle_.Load(MemoryOrder::RELAXED)),
454         GPR_CLOCK_REALTIME);
455     data["lastMessageReceivedTimestamp"] = gpr_format_timespec(ts);
456   }
457   int64_t keepalives_sent = keepalives_sent_.Load(MemoryOrder::RELAXED);
458   if (keepalives_sent != 0) {
459     data["keepAlivesSent"] = std::to_string(keepalives_sent);
460   }
461   // Create and fill the parent object.
462   Json::Object object = {
463       {"ref",
464        Json::Object{
465            {"socketId", std::to_string(uuid())},
466            {"name", name()},
467        }},
468       {"data", std::move(data)},
469   };
470   PopulateSocketAddressJson(&object, "remote", remote_.c_str());
471   PopulateSocketAddressJson(&object, "local", local_.c_str());
472   return object;
473 }
474 
475 //
476 // ListenSocketNode
477 //
478 
ListenSocketNode(std::string local_addr,std::string name)479 ListenSocketNode::ListenSocketNode(std::string local_addr, std::string name)
480     : BaseNode(EntityType::kSocket, std::move(name)),
481       local_addr_(std::move(local_addr)) {}
482 
RenderJson()483 Json ListenSocketNode::RenderJson() {
484   Json::Object object = {
485       {"ref",
486        Json::Object{
487            {"socketId", std::to_string(uuid())},
488            {"name", name()},
489        }},
490   };
491   PopulateSocketAddressJson(&object, "local", local_addr_.c_str());
492   return object;
493 }
494 
495 }  // namespace channelz
496 }  // namespace grpc_core
497