3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <grpc/impl/codegen/port_platform.h>
21 #include "src/core/lib/channel/channelz.h"
23 #include <grpc/grpc.h>
24 #include <grpc/support/alloc.h>
25 #include <grpc/support/log.h>
26 #include <grpc/support/string_util.h>
31 #include "src/core/lib/channel/channelz_registry.h"
32 #include "src/core/lib/channel/status_util.h"
33 #include "src/core/lib/gpr/string.h"
34 #include "src/core/lib/gpr/useful.h"
35 #include "src/core/lib/gprpp/atomic.h"
36 #include "src/core/lib/gprpp/host_port.h"
37 #include "src/core/lib/gprpp/memory.h"
38 #include "src/core/lib/iomgr/error.h"
39 #include "src/core/lib/iomgr/exec_ctx.h"
40 #include "src/core/lib/slice/b64.h"
41 #include "src/core/lib/slice/slice_internal.h"
42 #include "src/core/lib/surface/channel.h"
43 #include "src/core/lib/surface/server.h"
44 #include "src/core/lib/transport/connectivity_state.h"
45 #include "src/core/lib/transport/error_utils.h"
46 #include "src/core/lib/uri/uri_parser.h"
57 void* parent_uuid_copy(void* p) { return p; }
58 void parent_uuid_destroy(void* /*p*/) {}
59 int parent_uuid_cmp(void* p1, void* p2) { return GPR_ICMP(p1, p2); }
60 const grpc_arg_pointer_vtable parent_uuid_vtable = {
61 parent_uuid_copy, parent_uuid_destroy, parent_uuid_cmp};
65 grpc_arg MakeParentUuidArg(intptr_t parent_uuid) {
66 // We would ideally like to store the uuid in an integer argument.
67 // Unfortunately, that won't work, because intptr_t (the type used for
68 // uuids) doesn't fit in an int (the type used for integer args).
69 // So instead, we use a hack to store it as a pointer, because
70 // intptr_t should be the same size as void*.
71 static_assert(sizeof(intptr_t) <= sizeof(void*),
72 "can't fit intptr_t inside of void*");
73 return grpc_channel_arg_pointer_create(
74 const_cast<char*>(GRPC_ARG_CHANNELZ_PARENT_UUID),
75 reinterpret_cast<void*>(parent_uuid), &parent_uuid_vtable);
78 intptr_t GetParentUuidFromArgs(const grpc_channel_args& args) {
80 grpc_channel_args_find(&args, GRPC_ARG_CHANNELZ_PARENT_UUID);
81 if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return 0;
82 return reinterpret_cast<intptr_t>(arg->value.pointer.p);
89 BaseNode::BaseNode(EntityType type, std::string name)
90 : type_(type), uuid_(-1), name_(std::move(name)) {
91 // The registry will set uuid_ under its lock.
92 ChannelzRegistry::Register(this);
95 BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
97 char* BaseNode::RenderJsonString() {
98 grpc_json* json = RenderJson();
99 GPR_ASSERT(json != nullptr);
100 char* json_str = grpc_json_dump_to_string(json, 0);
101 grpc_json_destroy(json);
106 // CallCountingHelper
109 CallCountingHelper::CallCountingHelper() {
110 num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
111 per_cpu_counter_data_storage_.reserve(num_cores_);
112 for (size_t i = 0; i < num_cores_; ++i) {
113 per_cpu_counter_data_storage_.emplace_back();
117 void CallCountingHelper::RecordCallStarted() {
118 AtomicCounterData& data =
119 per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()];
120 data.calls_started.FetchAdd(1, MemoryOrder::RELAXED);
121 data.last_call_started_cycle.Store(gpr_get_cycle_counter(),
122 MemoryOrder::RELAXED);
125 void CallCountingHelper::RecordCallFailed() {
126 per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
127 .calls_failed.FetchAdd(1, MemoryOrder::RELAXED);
130 void CallCountingHelper::RecordCallSucceeded() {
131 per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
132 .calls_succeeded.FetchAdd(1, MemoryOrder::RELAXED);
135 void CallCountingHelper::CollectData(CounterData* out) {
136 for (size_t core = 0; core < num_cores_; ++core) {
137 AtomicCounterData& data = per_cpu_counter_data_storage_[core];
139 out->calls_started += data.calls_started.Load(MemoryOrder::RELAXED);
140 out->calls_succeeded +=
141 per_cpu_counter_data_storage_[core].calls_succeeded.Load(
142 MemoryOrder::RELAXED);
143 out->calls_failed += per_cpu_counter_data_storage_[core].calls_failed.Load(
144 MemoryOrder::RELAXED);
145 const gpr_cycle_counter last_call =
146 per_cpu_counter_data_storage_[core].last_call_started_cycle.Load(
147 MemoryOrder::RELAXED);
148 if (last_call > out->last_call_started_cycle) {
149 out->last_call_started_cycle = last_call;
154 void CallCountingHelper::PopulateCallCounts(grpc_json* json) {
155 grpc_json* json_iterator = nullptr;
158 if (data.calls_started != 0) {
159 json_iterator = grpc_json_add_number_string_child(
160 json, json_iterator, "callsStarted", data.calls_started);
162 if (data.calls_succeeded != 0) {
163 json_iterator = grpc_json_add_number_string_child(
164 json, json_iterator, "callsSucceeded", data.calls_succeeded);
166 if (data.calls_failed) {
167 json_iterator = grpc_json_add_number_string_child(
168 json, json_iterator, "callsFailed", data.calls_failed);
170 if (data.calls_started != 0) {
171 gpr_timespec ts = gpr_convert_clock_type(
172 gpr_cycle_counter_to_time(data.last_call_started_cycle),
175 grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
176 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
184 ChannelNode::ChannelNode(std::string target, size_t channel_tracer_max_nodes,
185 intptr_t parent_uuid)
186 : BaseNode(parent_uuid == 0 ? EntityType::kTopLevelChannel
187 : EntityType::kInternalChannel,
189 target_(std::move(target)),
190 trace_(channel_tracer_max_nodes),
191 parent_uuid_(parent_uuid) {}
193 const char* ChannelNode::GetChannelConnectivityStateChangeString(
194 grpc_connectivity_state state) {
196 case GRPC_CHANNEL_IDLE:
197 return "Channel state change to IDLE";
198 case GRPC_CHANNEL_CONNECTING:
199 return "Channel state change to CONNECTING";
200 case GRPC_CHANNEL_READY:
201 return "Channel state change to READY";
202 case GRPC_CHANNEL_TRANSIENT_FAILURE:
203 return "Channel state change to TRANSIENT_FAILURE";
204 case GRPC_CHANNEL_SHUTDOWN:
205 return "Channel state change to SHUTDOWN";
207 GPR_UNREACHABLE_CODE(return "UNKNOWN");
210 grpc_json* ChannelNode::RenderJson() {
211 // We need to track these three json objects to build our object
212 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
213 grpc_json* json = top_level_json;
214 grpc_json* json_iterator = nullptr;
215 // create and fill the ref child
216 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
217 GRPC_JSON_OBJECT, false);
218 json = json_iterator;
219 json_iterator = nullptr;
220 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
221 "channelId", uuid());
222 // reset json iterators to top level object
223 json = top_level_json;
224 json_iterator = nullptr;
225 // create and fill the data child.
226 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
227 GRPC_JSON_OBJECT, false);
229 json_iterator = nullptr;
230 // connectivity state
231 // If low-order bit is on, then the field is set.
232 int state_field = connectivity_state_.Load(MemoryOrder::RELAXED);
233 if ((state_field & 1) != 0) {
234 grpc_connectivity_state state =
235 static_cast<grpc_connectivity_state>(state_field >> 1);
236 json = grpc_json_create_child(nullptr, json, "state", nullptr,
237 GRPC_JSON_OBJECT, false);
238 grpc_json_create_child(nullptr, json, "state", ConnectivityStateName(state),
239 GRPC_JSON_STRING, false);
242 // populate the target.
243 GPR_ASSERT(!target_.empty());
244 grpc_json_create_child(nullptr, json, "target", target_.c_str(),
245 GRPC_JSON_STRING, false);
246 // fill in the channel trace if applicable
247 grpc_json* trace_json = trace_.RenderJson();
248 if (trace_json != nullptr) {
249 trace_json->key = "trace"; // this object is named trace in channelz.proto
250 grpc_json_link_child(json, trace_json, nullptr);
252 // ask CallCountingHelper to populate trace and call count data.
253 call_counter_.PopulateCallCounts(json);
254 json = top_level_json;
255 // template method. Child classes may override this to add their specific
257 PopulateChildRefs(json);
258 return top_level_json;
261 void ChannelNode::PopulateChildRefs(grpc_json* json) {
262 MutexLock lock(&child_mu_);
263 grpc_json* json_iterator = nullptr;
264 if (!child_subchannels_.empty()) {
265 grpc_json* array_parent = grpc_json_create_child(
266 nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false);
267 for (const auto& p : child_subchannels_) {
269 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
270 GRPC_JSON_OBJECT, false);
271 grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId",
275 if (!child_channels_.empty()) {
276 grpc_json* array_parent = grpc_json_create_child(
277 nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false);
278 json_iterator = nullptr;
279 for (const auto& p : child_channels_) {
281 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
282 GRPC_JSON_OBJECT, false);
283 grpc_json_add_number_string_child(json_iterator, nullptr, "channelId",
289 void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
290 // Store with low-order bit set to indicate that the field is set.
291 int state_field = (state << 1) + 1;
292 connectivity_state_.Store(state_field, MemoryOrder::RELAXED);
295 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
296 MutexLock lock(&child_mu_);
297 child_channels_.insert(std::make_pair(child_uuid, true));
300 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
301 MutexLock lock(&child_mu_);
302 child_channels_.erase(child_uuid);
305 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
306 MutexLock lock(&child_mu_);
307 child_subchannels_.insert(std::make_pair(child_uuid, true));
310 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
311 MutexLock lock(&child_mu_);
312 child_subchannels_.erase(child_uuid);
319 ServerNode::ServerNode(grpc_server* /*server*/, size_t channel_tracer_max_nodes)
320 : BaseNode(EntityType::kServer, ""), trace_(channel_tracer_max_nodes) {}
322 ServerNode::~ServerNode() {}
324 void ServerNode::AddChildSocket(RefCountedPtr<SocketNode> node) {
325 MutexLock lock(&child_mu_);
326 child_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
329 void ServerNode::RemoveChildSocket(intptr_t child_uuid) {
330 MutexLock lock(&child_mu_);
331 child_sockets_.erase(child_uuid);
334 void ServerNode::AddChildListenSocket(RefCountedPtr<ListenSocketNode> node) {
335 MutexLock lock(&child_mu_);
336 child_listen_sockets_.insert(std::make_pair(node->uuid(), std::move(node)));
339 void ServerNode::RemoveChildListenSocket(intptr_t child_uuid) {
340 MutexLock lock(&child_mu_);
341 child_listen_sockets_.erase(child_uuid);
344 char* ServerNode::RenderServerSockets(intptr_t start_socket_id,
345 intptr_t max_results) {
346 // If user does not set max_results, we choose 500.
347 size_t pagination_limit = max_results == 0 ? 500 : max_results;
348 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
349 grpc_json* json = top_level_json;
350 grpc_json* json_iterator = nullptr;
351 MutexLock lock(&child_mu_);
352 size_t sockets_rendered = 0;
353 if (!child_sockets_.empty()) {
354 // Create list of socket refs
355 grpc_json* array_parent = grpc_json_create_child(
356 nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
357 const size_t limit = GPR_MIN(child_sockets_.size(), pagination_limit);
358 for (auto it = child_sockets_.lower_bound(start_socket_id);
359 it != child_sockets_.end() && sockets_rendered < limit;
360 ++it, ++sockets_rendered) {
361 grpc_json* socket_ref_json = grpc_json_create_child(
362 nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false);
363 json_iterator = grpc_json_add_number_string_child(
364 socket_ref_json, nullptr, "socketId", it->first);
365 grpc_json_create_child(json_iterator, socket_ref_json, "name",
366 it->second->name().c_str(), GRPC_JSON_STRING,
370 if (sockets_rendered == child_sockets_.size()) {
371 json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
372 GRPC_JSON_TRUE, false);
374 char* json_str = grpc_json_dump_to_string(top_level_json, 0);
375 grpc_json_destroy(top_level_json);
379 grpc_json* ServerNode::RenderJson() {
380 // We need to track these three json objects to build our object
381 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
382 grpc_json* json = top_level_json;
383 grpc_json* json_iterator = nullptr;
384 // create and fill the ref child
385 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
386 GRPC_JSON_OBJECT, false);
387 json = json_iterator;
388 json_iterator = nullptr;
389 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
391 // reset json iterators to top level object
392 json = top_level_json;
393 json_iterator = nullptr;
394 // create and fill the data child.
395 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
396 GRPC_JSON_OBJECT, false);
398 json_iterator = nullptr;
399 // fill in the channel trace if applicable
400 grpc_json* trace_json = trace_.RenderJson();
401 if (trace_json != nullptr) {
402 trace_json->key = "trace"; // this object is named trace in channelz.proto
403 grpc_json_link_child(json, trace_json, nullptr);
405 // ask CallCountingHelper to populate trace and call count data.
406 call_counter_.PopulateCallCounts(json);
407 json = top_level_json;
408 // Render listen sockets
409 MutexLock lock(&child_mu_);
410 if (!child_listen_sockets_.empty()) {
411 grpc_json* array_parent = grpc_json_create_child(
412 nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false);
413 for (const auto& it : child_listen_sockets_) {
415 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
416 GRPC_JSON_OBJECT, false);
417 grpc_json* sibling_iterator = grpc_json_add_number_string_child(
418 json_iterator, nullptr, "socketId", it.first);
419 grpc_json_create_child(sibling_iterator, json_iterator, "name",
420 it.second->name().c_str(), GRPC_JSON_STRING,
424 return top_level_json;
433 void PopulateSocketAddressJson(grpc_json* json, const char* name,
434 const char* addr_str) {
435 if (addr_str == nullptr) return;
436 grpc_json* json_iterator = nullptr;
437 json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr,
438 GRPC_JSON_OBJECT, false);
439 json = json_iterator;
440 json_iterator = nullptr;
441 grpc_uri* uri = grpc_uri_parse(addr_str, true);
442 if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) ||
443 (strcmp(uri->scheme, "ipv6") == 0))) {
444 const char* host_port = uri->path;
445 if (*host_port == '/') ++host_port;
446 grpc_core::UniquePtr<char> host;
447 grpc_core::UniquePtr<char> port;
448 GPR_ASSERT(SplitHostPort(host_port, &host, &port));
450 if (port != nullptr) {
451 port_num = atoi(port.get());
454 grpc_base64_encode(host.get(), strlen(host.get()), false, false);
455 json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address",
456 nullptr, GRPC_JSON_OBJECT, false);
457 json = json_iterator;
458 json_iterator = nullptr;
459 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
461 json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
462 b64_host, GRPC_JSON_STRING, true);
463 } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) {
464 json_iterator = grpc_json_create_child(json_iterator, json, "uds_address",
465 nullptr, GRPC_JSON_OBJECT, false);
466 json = json_iterator;
467 json_iterator = nullptr;
469 grpc_json_create_child(json_iterator, json, "filename",
470 gpr_strdup(uri->path), GRPC_JSON_STRING, true);
472 json_iterator = grpc_json_create_child(json_iterator, json, "other_address",
473 nullptr, GRPC_JSON_OBJECT, false);
474 json = json_iterator;
475 json_iterator = nullptr;
476 json_iterator = grpc_json_create_child(json_iterator, json, "name",
477 addr_str, GRPC_JSON_STRING, false);
479 grpc_uri_destroy(uri);
484 SocketNode::SocketNode(std::string local, std::string remote, std::string name)
485 : BaseNode(EntityType::kSocket, std::move(name)),
486 local_(std::move(local)),
487 remote_(std::move(remote)) {}
489 void SocketNode::RecordStreamStartedFromLocal() {
490 streams_started_.FetchAdd(1, MemoryOrder::RELAXED);
491 last_local_stream_created_cycle_.Store(gpr_get_cycle_counter(),
492 MemoryOrder::RELAXED);
495 void SocketNode::RecordStreamStartedFromRemote() {
496 streams_started_.FetchAdd(1, MemoryOrder::RELAXED);
497 last_remote_stream_created_cycle_.Store(gpr_get_cycle_counter(),
498 MemoryOrder::RELAXED);
501 void SocketNode::RecordMessagesSent(uint32_t num_sent) {
502 messages_sent_.FetchAdd(num_sent, MemoryOrder::RELAXED);
503 last_message_sent_cycle_.Store(gpr_get_cycle_counter(), MemoryOrder::RELAXED);
506 void SocketNode::RecordMessageReceived() {
507 messages_received_.FetchAdd(1, MemoryOrder::RELAXED);
508 last_message_received_cycle_.Store(gpr_get_cycle_counter(),
509 MemoryOrder::RELAXED);
512 grpc_json* SocketNode::RenderJson() {
513 // We need to track these three json objects to build our object
514 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
515 grpc_json* json = top_level_json;
516 grpc_json* json_iterator = nullptr;
517 // create and fill the ref child
518 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
519 GRPC_JSON_OBJECT, false);
520 json = json_iterator;
521 json_iterator = nullptr;
522 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
524 json_iterator = grpc_json_create_child(
525 json_iterator, json, "name", name().c_str(), GRPC_JSON_STRING, false);
526 json = top_level_json;
527 PopulateSocketAddressJson(json, "remote", remote_.c_str());
528 PopulateSocketAddressJson(json, "local", local_.c_str());
529 // reset json iterators to top level object
530 json = top_level_json;
531 json_iterator = nullptr;
532 // create and fill the data child.
533 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
534 GRPC_JSON_OBJECT, false);
536 json_iterator = nullptr;
538 int64_t streams_started = streams_started_.Load(MemoryOrder::RELAXED);
539 if (streams_started != 0) {
540 json_iterator = grpc_json_add_number_string_child(
541 json, json_iterator, "streamsStarted", streams_started);
542 gpr_cycle_counter last_local_stream_created_cycle =
543 last_local_stream_created_cycle_.Load(MemoryOrder::RELAXED);
544 if (last_local_stream_created_cycle != 0) {
545 ts = gpr_convert_clock_type(
546 gpr_cycle_counter_to_time(last_local_stream_created_cycle),
548 json_iterator = grpc_json_create_child(
549 json_iterator, json, "lastLocalStreamCreatedTimestamp",
550 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
552 gpr_cycle_counter last_remote_stream_created_cycle =
553 last_remote_stream_created_cycle_.Load(MemoryOrder::RELAXED);
554 if (last_remote_stream_created_cycle != 0) {
555 ts = gpr_convert_clock_type(
556 gpr_cycle_counter_to_time(last_remote_stream_created_cycle),
558 json_iterator = grpc_json_create_child(
559 json_iterator, json, "lastRemoteStreamCreatedTimestamp",
560 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
563 int64_t streams_succeeded = streams_succeeded_.Load(MemoryOrder::RELAXED);
564 if (streams_succeeded != 0) {
565 json_iterator = grpc_json_add_number_string_child(
566 json, json_iterator, "streamsSucceeded", streams_succeeded);
568 int64_t streams_failed = streams_failed_.Load(MemoryOrder::RELAXED);
569 if (streams_failed) {
570 json_iterator = grpc_json_add_number_string_child(
571 json, json_iterator, "streamsFailed", streams_failed);
573 int64_t messages_sent = messages_sent_.Load(MemoryOrder::RELAXED);
574 if (messages_sent != 0) {
575 json_iterator = grpc_json_add_number_string_child(
576 json, json_iterator, "messagesSent", messages_sent);
577 ts = gpr_convert_clock_type(
578 gpr_cycle_counter_to_time(
579 last_message_sent_cycle_.Load(MemoryOrder::RELAXED)),
582 grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp",
583 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
585 int64_t messages_received = messages_received_.Load(MemoryOrder::RELAXED);
586 if (messages_received != 0) {
587 json_iterator = grpc_json_add_number_string_child(
588 json, json_iterator, "messagesReceived", messages_received);
589 ts = gpr_convert_clock_type(
590 gpr_cycle_counter_to_time(
591 last_message_received_cycle_.Load(MemoryOrder::RELAXED)),
593 json_iterator = grpc_json_create_child(
594 json_iterator, json, "lastMessageReceivedTimestamp",
595 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
597 int64_t keepalives_sent = keepalives_sent_.Load(MemoryOrder::RELAXED);
598 if (keepalives_sent != 0) {
599 json_iterator = grpc_json_add_number_string_child(
600 json, json_iterator, "keepAlivesSent", keepalives_sent);
602 return top_level_json;
609 ListenSocketNode::ListenSocketNode(std::string local_addr, std::string name)
610 : BaseNode(EntityType::kSocket, std::move(name)),
611 local_addr_(std::move(local_addr)) {}
613 grpc_json* ListenSocketNode::RenderJson() {
614 // We need to track these three json objects to build our object
615 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
616 grpc_json* json = top_level_json;
617 grpc_json* json_iterator = nullptr;
618 // create and fill the ref child
619 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
620 GRPC_JSON_OBJECT, false);
621 json = json_iterator;
622 json_iterator = nullptr;
623 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
625 json_iterator = grpc_json_create_child(
626 json_iterator, json, "name", name().c_str(), GRPC_JSON_STRING, false);
627 json = top_level_json;
628 PopulateSocketAddressJson(json, "local", local_addr_.c_str());
630 return top_level_json;
633 } // namespace channelz
634 } // namespace grpc_core