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/support/port_platform.h>
20 
21 #include "src/core/lib/channel/channelz.h"
22 
23 #include <algorithm>
24 #include <atomic>
25 #include <cstdint>
26 
27 #include "absl/status/statusor.h"
28 #include "absl/strings/escaping.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/strip.h"
31 
32 #include <grpc/support/json.h>
33 #include <grpc/support/log.h>
34 #include <grpc/support/time.h>
35 
36 #include "src/core/lib/address_utils/parse_address.h"
37 #include "src/core/lib/address_utils/sockaddr_utils.h"
38 #include "src/core/lib/channel/channel_args.h"
39 #include "src/core/lib/channel/channelz_registry.h"
40 #include "src/core/lib/gpr/string.h"
41 #include "src/core/lib/gpr/useful.h"
42 #include "src/core/lib/iomgr/resolved_address.h"
43 #include "src/core/lib/json/json_writer.h"
44 #include "src/core/lib/transport/connectivity_state.h"
45 #include "src/core/lib/uri/uri_parser.h"
46 
47 namespace grpc_core {
48 namespace channelz {
49 
50 //
51 // BaseNode
52 //
53 
BaseNode(EntityType type,std::string name)54 BaseNode::BaseNode(EntityType type, std::string name)
55     : type_(type), uuid_(-1), name_(std::move(name)) {
56   // The registry will set uuid_ under its lock.
57   ChannelzRegistry::Register(this);
58 }
59 
~BaseNode()60 BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
61 
RenderJsonString()62 std::string BaseNode::RenderJsonString() {
63   Json json = RenderJson();
64   return JsonDump(json);
65 }
66 
67 //
68 // CallCountingHelper
69 //
70 
RecordCallStarted()71 void CallCountingHelper::RecordCallStarted() {
72   calls_started_.fetch_add(1, std::memory_order_relaxed);
73   last_call_started_cycle_.store(gpr_get_cycle_counter(),
74                                  std::memory_order_relaxed);
75 }
76 
RecordCallFailed()77 void CallCountingHelper::RecordCallFailed() {
78   calls_failed_.fetch_add(1, std::memory_order_relaxed);
79 }
80 
RecordCallSucceeded()81 void CallCountingHelper::RecordCallSucceeded() {
82   calls_succeeded_.fetch_add(1, std::memory_order_relaxed);
83 }
84 
PopulateCallCounts(Json::Object * json)85 void CallCountingHelper::PopulateCallCounts(Json::Object* json) {
86   auto calls_started = calls_started_.load(std::memory_order_relaxed);
87   auto calls_succeeded = calls_succeeded_.load(std::memory_order_relaxed);
88   auto calls_failed = calls_failed_.load(std::memory_order_relaxed);
89   auto last_call_started_cycle =
90       last_call_started_cycle_.load(std::memory_order_relaxed);
91   if (calls_started != 0) {
92     (*json)["callsStarted"] = Json::FromString(absl::StrCat(calls_started));
93     gpr_timespec ts = gpr_convert_clock_type(
94         gpr_cycle_counter_to_time(last_call_started_cycle), GPR_CLOCK_REALTIME);
95     (*json)["lastCallStartedTimestamp"] =
96         Json::FromString(gpr_format_timespec(ts));
97   }
98   if (calls_succeeded != 0) {
99     (*json)["callsSucceeded"] = Json::FromString(absl::StrCat(calls_succeeded));
100   }
101   if (calls_failed != 0) {
102     (*json)["callsFailed"] = Json::FromString(absl::StrCat(calls_failed));
103   }
104 }
105 
106 //
107 // PerCpuCallCountingHelper
108 //
109 
RecordCallStarted()110 void PerCpuCallCountingHelper::RecordCallStarted() {
111   auto& data = per_cpu_data_.this_cpu();
112   data.calls_started.fetch_add(1, std::memory_order_relaxed);
113   data.last_call_started_cycle.store(gpr_get_cycle_counter(),
114                                      std::memory_order_relaxed);
115 }
116 
RecordCallFailed()117 void PerCpuCallCountingHelper::RecordCallFailed() {
118   per_cpu_data_.this_cpu().calls_failed.fetch_add(1, std::memory_order_relaxed);
119 }
120 
RecordCallSucceeded()121 void PerCpuCallCountingHelper::RecordCallSucceeded() {
122   per_cpu_data_.this_cpu().calls_succeeded.fetch_add(1,
123                                                      std::memory_order_relaxed);
124 }
125 
PopulateCallCounts(Json::Object * json)126 void PerCpuCallCountingHelper::PopulateCallCounts(Json::Object* json) {
127   int64_t calls_started = 0;
128   int64_t calls_succeeded = 0;
129   int64_t calls_failed = 0;
130   gpr_cycle_counter last_call_started_cycle = 0;
131   for (const auto& cpu : per_cpu_data_) {
132     calls_started += cpu.calls_started.load(std::memory_order_relaxed);
133     calls_succeeded += cpu.calls_succeeded.load(std::memory_order_relaxed);
134     calls_failed += cpu.calls_failed.load(std::memory_order_relaxed);
135     last_call_started_cycle =
136         std::max(last_call_started_cycle,
137                  cpu.last_call_started_cycle.load(std::memory_order_relaxed));
138   }
139 
140   if (calls_started != 0) {
141     (*json)["callsStarted"] = Json::FromString(absl::StrCat(calls_started));
142     gpr_timespec ts = gpr_convert_clock_type(
143         gpr_cycle_counter_to_time(last_call_started_cycle), GPR_CLOCK_REALTIME);
144     (*json)["lastCallStartedTimestamp"] =
145         Json::FromString(gpr_format_timespec(ts));
146   }
147   if (calls_succeeded != 0) {
148     (*json)["callsSucceeded"] = Json::FromString(absl::StrCat(calls_succeeded));
149   }
150   if (calls_failed != 0) {
151     (*json)["callsFailed"] = Json::FromString(absl::StrCat(calls_failed));
152   }
153 }
154 
155 //
156 // ChannelNode
157 //
158 
ChannelNode(std::string target,size_t channel_tracer_max_nodes,bool is_internal_channel)159 ChannelNode::ChannelNode(std::string target, size_t channel_tracer_max_nodes,
160                          bool is_internal_channel)
161     : BaseNode(is_internal_channel ? EntityType::kInternalChannel
162                                    : EntityType::kTopLevelChannel,
163                target),
164       target_(std::move(target)),
165       trace_(channel_tracer_max_nodes) {}
166 
GetChannelConnectivityStateChangeString(grpc_connectivity_state state)167 const char* ChannelNode::GetChannelConnectivityStateChangeString(
168     grpc_connectivity_state state) {
169   switch (state) {
170     case GRPC_CHANNEL_IDLE:
171       return "Channel state change to IDLE";
172     case GRPC_CHANNEL_CONNECTING:
173       return "Channel state change to CONNECTING";
174     case GRPC_CHANNEL_READY:
175       return "Channel state change to READY";
176     case GRPC_CHANNEL_TRANSIENT_FAILURE:
177       return "Channel state change to TRANSIENT_FAILURE";
178     case GRPC_CHANNEL_SHUTDOWN:
179       return "Channel state change to SHUTDOWN";
180   }
181   GPR_UNREACHABLE_CODE(return "UNKNOWN");
182 }
183 
RenderJson()184 Json ChannelNode::RenderJson() {
185   Json::Object data = {
186       {"target", Json::FromString(target_)},
187   };
188   // Connectivity state.
189   // If low-order bit is on, then the field is set.
190   int state_field = connectivity_state_.load(std::memory_order_relaxed);
191   if ((state_field & 1) != 0) {
192     grpc_connectivity_state state =
193         static_cast<grpc_connectivity_state>(state_field >> 1);
194     data["state"] = Json::FromObject({
195         {"state", Json::FromString(ConnectivityStateName(state))},
196     });
197   }
198   // Fill in the channel trace if applicable.
199   Json trace_json = trace_.RenderJson();
200   if (trace_json.type() != Json::Type::kNull) {
201     data["trace"] = std::move(trace_json);
202   }
203   // Ask CallCountingHelper to populate call count data.
204   call_counter_.PopulateCallCounts(&data);
205   // Construct outer object.
206   Json::Object json = {
207       {"ref", Json::FromObject({
208                   {"channelId", Json::FromString(absl::StrCat(uuid()))},
209               })},
210       {"data", Json::FromObject(std::move(data))},
211   };
212   // Template method. Child classes may override this to add their specific
213   // functionality.
214   PopulateChildRefs(&json);
215   return Json::FromObject(std::move(json));
216 }
217 
PopulateChildRefs(Json::Object * json)218 void ChannelNode::PopulateChildRefs(Json::Object* json) {
219   MutexLock lock(&child_mu_);
220   if (!child_subchannels_.empty()) {
221     Json::Array array;
222     for (intptr_t subchannel_uuid : child_subchannels_) {
223       array.emplace_back(Json::FromObject({
224           {"subchannelId", Json::FromString(absl::StrCat(subchannel_uuid))},
225       }));
226     }
227     (*json)["subchannelRef"] = Json::FromArray(std::move(array));
228   }
229   if (!child_channels_.empty()) {
230     Json::Array array;
231     for (intptr_t channel_uuid : child_channels_) {
232       array.emplace_back(Json::FromObject({
233           {"channelId", Json::FromString(absl::StrCat(channel_uuid))},
234       }));
235     }
236     (*json)["channelRef"] = Json::FromArray(std::move(array));
237   }
238 }
239 
SetConnectivityState(grpc_connectivity_state state)240 void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
241   // Store with low-order bit set to indicate that the field is set.
242   int state_field = (state << 1) + 1;
243   connectivity_state_.store(state_field, std::memory_order_relaxed);
244 }
245 
AddChildChannel(intptr_t child_uuid)246 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
247   MutexLock lock(&child_mu_);
248   child_channels_.insert(child_uuid);
249 }
250 
RemoveChildChannel(intptr_t child_uuid)251 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
252   MutexLock lock(&child_mu_);
253   child_channels_.erase(child_uuid);
254 }
255 
AddChildSubchannel(intptr_t child_uuid)256 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
257   MutexLock lock(&child_mu_);
258   child_subchannels_.insert(child_uuid);
259 }
260 
RemoveChildSubchannel(intptr_t child_uuid)261 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
262   MutexLock lock(&child_mu_);
263   child_subchannels_.erase(child_uuid);
264 }
265 
266 //
267 // ServerNode
268 //
269 
ServerNode(size_t channel_tracer_max_nodes)270 ServerNode::ServerNode(size_t channel_tracer_max_nodes)
271     : BaseNode(EntityType::kServer, ""), trace_(channel_tracer_max_nodes) {}
272 
~ServerNode()273 ServerNode::~ServerNode() {}
274 
AddChildSocket(RefCountedPtr<SocketNode> node)275 void ServerNode::AddChildSocket(RefCountedPtr<SocketNode> node) {
276   MutexLock lock(&child_mu_);
277   child_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
278 }
279 
RemoveChildSocket(intptr_t child_uuid)280 void ServerNode::RemoveChildSocket(intptr_t child_uuid) {
281   MutexLock lock(&child_mu_);
282   child_sockets_.erase(child_uuid);
283 }
284 
AddChildListenSocket(RefCountedPtr<ListenSocketNode> node)285 void ServerNode::AddChildListenSocket(RefCountedPtr<ListenSocketNode> node) {
286   MutexLock lock(&child_mu_);
287   child_listen_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
288 }
289 
RemoveChildListenSocket(intptr_t child_uuid)290 void ServerNode::RemoveChildListenSocket(intptr_t child_uuid) {
291   MutexLock lock(&child_mu_);
292   child_listen_sockets_.erase(child_uuid);
293 }
294 
RenderServerSockets(intptr_t start_socket_id,intptr_t max_results)295 std::string ServerNode::RenderServerSockets(intptr_t start_socket_id,
296                                             intptr_t max_results) {
297   GPR_ASSERT(start_socket_id >= 0);
298   GPR_ASSERT(max_results >= 0);
299   // If user does not set max_results, we choose 500.
300   size_t pagination_limit = max_results == 0 ? 500 : max_results;
301   Json::Object object;
302   {
303     MutexLock lock(&child_mu_);
304     size_t sockets_rendered = 0;
305     // Create list of socket refs.
306     Json::Array array;
307     auto it = child_sockets_.lower_bound(start_socket_id);
308     for (; it != child_sockets_.end() && sockets_rendered < pagination_limit;
309          ++it, ++sockets_rendered) {
310       array.emplace_back(Json::FromObject({
311           {"socketId", Json::FromString(absl::StrCat(it->first))},
312           {"name", Json::FromString(it->second->name())},
313       }));
314     }
315     object["socketRef"] = Json::FromArray(std::move(array));
316     if (it == child_sockets_.end()) {
317       object["end"] = Json::FromBool(true);
318     }
319   }
320   return JsonDump(Json::FromObject(std::move(object)));
321 }
322 
RenderJson()323 Json ServerNode::RenderJson() {
324   Json::Object data;
325   // Fill in the channel trace if applicable.
326   Json trace_json = trace_.RenderJson();
327   if (trace_json.type() != Json::Type::kNull) {
328     data["trace"] = std::move(trace_json);
329   }
330   // Ask CallCountingHelper to populate call count data.
331   call_counter_.PopulateCallCounts(&data);
332   // Construct top-level object.
333   Json::Object object = {
334       {"ref", Json::FromObject({
335                   {"serverId", Json::FromString(absl::StrCat(uuid()))},
336               })},
337       {"data", Json::FromObject(std::move(data))},
338   };
339   // Render listen sockets.
340   {
341     MutexLock lock(&child_mu_);
342     if (!child_listen_sockets_.empty()) {
343       Json::Array array;
344       for (const auto& it : child_listen_sockets_) {
345         array.emplace_back(Json::FromObject({
346             {"socketId", Json::FromString(absl::StrCat(it.first))},
347             {"name", Json::FromString(it.second->name())},
348         }));
349       }
350       object["listenSocket"] = Json::FromArray(std::move(array));
351     }
352   }
353   return Json::FromObject(std::move(object));
354 }
355 
356 //
357 // SocketNode::Security::Tls
358 //
359 
RenderJson()360 Json SocketNode::Security::Tls::RenderJson() {
361   Json::Object data;
362   if (type == NameType::kStandardName) {
363     data["standard_name"] = Json::FromString(name);
364   } else if (type == NameType::kOtherName) {
365     data["other_name"] = Json::FromString(name);
366   }
367   if (!local_certificate.empty()) {
368     data["local_certificate"] =
369         Json::FromString(absl::Base64Escape(local_certificate));
370   }
371   if (!remote_certificate.empty()) {
372     data["remote_certificate"] =
373         Json::FromString(absl::Base64Escape(remote_certificate));
374   }
375   return Json::FromObject(std::move(data));
376 }
377 
378 //
379 // SocketNode::Security
380 //
381 
RenderJson()382 Json SocketNode::Security::RenderJson() {
383   Json::Object data;
384   switch (type) {
385     case ModelType::kUnset:
386       break;
387     case ModelType::kTls:
388       if (tls) {
389         data["tls"] = tls->RenderJson();
390       }
391       break;
392     case ModelType::kOther:
393       if (other.has_value()) {
394         data["other"] = *other;
395       }
396       break;
397   }
398   return Json::FromObject(std::move(data));
399 }
400 
401 namespace {
402 
SecurityArgCopy(void * p)403 void* SecurityArgCopy(void* p) {
404   SocketNode::Security* xds_certificate_provider =
405       static_cast<SocketNode::Security*>(p);
406   return xds_certificate_provider->Ref().release();
407 }
408 
SecurityArgDestroy(void * p)409 void SecurityArgDestroy(void* p) {
410   SocketNode::Security* xds_certificate_provider =
411       static_cast<SocketNode::Security*>(p);
412   xds_certificate_provider->Unref();
413 }
414 
SecurityArgCmp(void * p,void * q)415 int SecurityArgCmp(void* p, void* q) { return QsortCompare(p, q); }
416 
417 const grpc_arg_pointer_vtable kChannelArgVtable = {
418     SecurityArgCopy, SecurityArgDestroy, SecurityArgCmp};
419 
420 }  // namespace
421 
MakeChannelArg() const422 grpc_arg SocketNode::Security::MakeChannelArg() const {
423   return grpc_channel_arg_pointer_create(
424       const_cast<char*>(GRPC_ARG_CHANNELZ_SECURITY),
425       const_cast<SocketNode::Security*>(this), &kChannelArgVtable);
426 }
427 
GetFromChannelArgs(const grpc_channel_args * args)428 RefCountedPtr<SocketNode::Security> SocketNode::Security::GetFromChannelArgs(
429     const grpc_channel_args* args) {
430   Security* security = grpc_channel_args_find_pointer<Security>(
431       args, GRPC_ARG_CHANNELZ_SECURITY);
432   return security != nullptr ? security->Ref() : nullptr;
433 }
434 
435 //
436 // SocketNode
437 //
438 
439 namespace {
440 
PopulateSocketAddressJson(Json::Object * json,const char * name,const char * addr_str)441 void PopulateSocketAddressJson(Json::Object* json, const char* name,
442                                const char* addr_str) {
443   if (addr_str == nullptr) return;
444   absl::StatusOr<URI> uri = URI::Parse(addr_str);
445   if (uri.ok()) {
446     if (uri->scheme() == "ipv4" || uri->scheme() == "ipv6") {
447       auto address = StringToSockaddr(absl::StripPrefix(uri->path(), "/"));
448       if (address.ok()) {
449         std::string packed_host = grpc_sockaddr_get_packed_host(&*address);
450         (*json)[name] = Json::FromObject({
451             {"tcpip_address",
452              Json::FromObject({
453                  {"port", Json::FromString(
454                               absl::StrCat(grpc_sockaddr_get_port(&*address)))},
455                  {"ip_address",
456                   Json::FromString(absl::Base64Escape(packed_host))},
457              })},
458         });
459         return;
460       }
461     } else if (uri->scheme() == "unix") {
462       (*json)[name] = Json::FromObject({
463           {"uds_address", Json::FromObject({
464                               {"filename", Json::FromString(uri->path())},
465                           })},
466       });
467       return;
468     }
469   }
470   // Unknown address type.
471   (*json)[name] = Json::FromObject({
472       {"other_address", Json::FromObject({
473                             {"name", Json::FromString(addr_str)},
474                         })},
475   });
476 }
477 
478 }  // namespace
479 
SocketNode(std::string local,std::string remote,std::string name,RefCountedPtr<Security> security)480 SocketNode::SocketNode(std::string local, std::string remote, std::string name,
481                        RefCountedPtr<Security> security)
482     : BaseNode(EntityType::kSocket, std::move(name)),
483       local_(std::move(local)),
484       remote_(std::move(remote)),
485       security_(std::move(security)) {}
486 
RecordStreamStartedFromLocal()487 void SocketNode::RecordStreamStartedFromLocal() {
488   streams_started_.fetch_add(1, std::memory_order_relaxed);
489   last_local_stream_created_cycle_.store(gpr_get_cycle_counter(),
490                                          std::memory_order_relaxed);
491 }
492 
RecordStreamStartedFromRemote()493 void SocketNode::RecordStreamStartedFromRemote() {
494   streams_started_.fetch_add(1, std::memory_order_relaxed);
495   last_remote_stream_created_cycle_.store(gpr_get_cycle_counter(),
496                                           std::memory_order_relaxed);
497 }
498 
RecordMessagesSent(uint32_t num_sent)499 void SocketNode::RecordMessagesSent(uint32_t num_sent) {
500   messages_sent_.fetch_add(num_sent, std::memory_order_relaxed);
501   last_message_sent_cycle_.store(gpr_get_cycle_counter(),
502                                  std::memory_order_relaxed);
503 }
504 
RecordMessageReceived()505 void SocketNode::RecordMessageReceived() {
506   messages_received_.fetch_add(1, std::memory_order_relaxed);
507   last_message_received_cycle_.store(gpr_get_cycle_counter(),
508                                      std::memory_order_relaxed);
509 }
510 
RenderJson()511 Json SocketNode::RenderJson() {
512   // Create and fill the data child.
513   Json::Object data;
514   gpr_timespec ts;
515   int64_t streams_started = streams_started_.load(std::memory_order_relaxed);
516   if (streams_started != 0) {
517     data["streamsStarted"] = Json::FromString(absl::StrCat(streams_started));
518     gpr_cycle_counter last_local_stream_created_cycle =
519         last_local_stream_created_cycle_.load(std::memory_order_relaxed);
520     if (last_local_stream_created_cycle != 0) {
521       ts = gpr_convert_clock_type(
522           gpr_cycle_counter_to_time(last_local_stream_created_cycle),
523           GPR_CLOCK_REALTIME);
524       data["lastLocalStreamCreatedTimestamp"] =
525           Json::FromString(gpr_format_timespec(ts));
526     }
527     gpr_cycle_counter last_remote_stream_created_cycle =
528         last_remote_stream_created_cycle_.load(std::memory_order_relaxed);
529     if (last_remote_stream_created_cycle != 0) {
530       ts = gpr_convert_clock_type(
531           gpr_cycle_counter_to_time(last_remote_stream_created_cycle),
532           GPR_CLOCK_REALTIME);
533       data["lastRemoteStreamCreatedTimestamp"] =
534           Json::FromString(gpr_format_timespec(ts));
535     }
536   }
537   int64_t streams_succeeded =
538       streams_succeeded_.load(std::memory_order_relaxed);
539   if (streams_succeeded != 0) {
540     data["streamsSucceeded"] =
541         Json::FromString(absl::StrCat(streams_succeeded));
542   }
543   int64_t streams_failed = streams_failed_.load(std::memory_order_relaxed);
544   if (streams_failed != 0) {
545     data["streamsFailed"] = Json::FromString(absl::StrCat(streams_failed));
546   }
547   int64_t messages_sent = messages_sent_.load(std::memory_order_relaxed);
548   if (messages_sent != 0) {
549     data["messagesSent"] = Json::FromString(absl::StrCat(messages_sent));
550     ts = gpr_convert_clock_type(
551         gpr_cycle_counter_to_time(
552             last_message_sent_cycle_.load(std::memory_order_relaxed)),
553         GPR_CLOCK_REALTIME);
554     data["lastMessageSentTimestamp"] =
555         Json::FromString(gpr_format_timespec(ts));
556   }
557   int64_t messages_received =
558       messages_received_.load(std::memory_order_relaxed);
559   if (messages_received != 0) {
560     data["messagesReceived"] =
561         Json::FromString(absl::StrCat(messages_received));
562     ts = gpr_convert_clock_type(
563         gpr_cycle_counter_to_time(
564             last_message_received_cycle_.load(std::memory_order_relaxed)),
565         GPR_CLOCK_REALTIME);
566     data["lastMessageReceivedTimestamp"] =
567         Json::FromString(gpr_format_timespec(ts));
568   }
569   int64_t keepalives_sent = keepalives_sent_.load(std::memory_order_relaxed);
570   if (keepalives_sent != 0) {
571     data["keepAlivesSent"] = Json::FromString(absl::StrCat(keepalives_sent));
572   }
573   // Create and fill the parent object.
574   Json::Object object = {
575       {"ref", Json::FromObject({
576                   {"socketId", Json::FromString(absl::StrCat(uuid()))},
577                   {"name", Json::FromString(name())},
578               })},
579       {"data", Json::FromObject(std::move(data))},
580   };
581   if (security_ != nullptr &&
582       security_->type != SocketNode::Security::ModelType::kUnset) {
583     object["security"] = security_->RenderJson();
584   }
585   PopulateSocketAddressJson(&object, "remote", remote_.c_str());
586   PopulateSocketAddressJson(&object, "local", local_.c_str());
587   return Json::FromObject(std::move(object));
588 }
589 
590 //
591 // ListenSocketNode
592 //
593 
ListenSocketNode(std::string local_addr,std::string name)594 ListenSocketNode::ListenSocketNode(std::string local_addr, std::string name)
595     : BaseNode(EntityType::kSocket, std::move(name)),
596       local_addr_(std::move(local_addr)) {}
597 
RenderJson()598 Json ListenSocketNode::RenderJson() {
599   Json::Object object = {
600       {"ref", Json::FromObject({
601                   {"socketId", Json::FromString(absl::StrCat(uuid()))},
602                   {"name", Json::FromString(name())},
603               })},
604   };
605   PopulateSocketAddressJson(&object, "local", local_addr_.c_str());
606   return Json::FromObject(std::move(object));
607 }
608 
609 }  // namespace channelz
610 }  // namespace grpc_core
611