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/host_port.h"
36 #include "src/core/lib/gprpp/memory.h"
37 #include "src/core/lib/iomgr/error.h"
38 #include "src/core/lib/iomgr/exec_ctx.h"
39 #include "src/core/lib/slice/b64.h"
40 #include "src/core/lib/slice/slice_internal.h"
41 #include "src/core/lib/surface/channel.h"
42 #include "src/core/lib/surface/server.h"
43 #include "src/core/lib/transport/connectivity_state.h"
44 #include "src/core/lib/transport/error_utils.h"
45 #include "src/core/lib/uri/uri_parser.h"
56 void* parent_uuid_copy(void* p) { return p; }
57 void parent_uuid_destroy(void* p) {}
58 int parent_uuid_cmp(void* p1, void* p2) { return GPR_ICMP(p1, p2); }
59 const grpc_arg_pointer_vtable parent_uuid_vtable = {
60 parent_uuid_copy, parent_uuid_destroy, parent_uuid_cmp};
64 grpc_arg MakeParentUuidArg(intptr_t parent_uuid) {
65 // We would ideally like to store the uuid in an integer argument.
66 // Unfortunately, that won't work, because intptr_t (the type used for
67 // uuids) doesn't fit in an int (the type used for integer args).
68 // So instead, we use a hack to store it as a pointer, because
69 // intptr_t should be the same size as void*.
70 static_assert(sizeof(intptr_t) <= sizeof(void*),
71 "can't fit intptr_t inside of void*");
72 return grpc_channel_arg_pointer_create(
73 const_cast<char*>(GRPC_ARG_CHANNELZ_PARENT_UUID),
74 reinterpret_cast<void*>(parent_uuid), &parent_uuid_vtable);
77 intptr_t GetParentUuidFromArgs(const grpc_channel_args& args) {
79 grpc_channel_args_find(&args, GRPC_ARG_CHANNELZ_PARENT_UUID);
80 if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return 0;
81 return reinterpret_cast<intptr_t>(arg->value.pointer.p);
88 BaseNode::BaseNode(EntityType type, UniquePtr<char> name)
89 : type_(type), uuid_(-1), name_(std::move(name)) {
90 // The registry will set uuid_ under its lock.
91 ChannelzRegistry::Register(this);
94 BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
96 char* BaseNode::RenderJsonString() {
97 grpc_json* json = RenderJson();
98 GPR_ASSERT(json != nullptr);
99 char* json_str = grpc_json_dump_to_string(json, 0);
100 grpc_json_destroy(json);
105 // CallCountingHelper
108 CallCountingHelper::CallCountingHelper() {
109 num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
110 per_cpu_counter_data_storage_ = static_cast<AtomicCounterData*>(
111 gpr_zalloc(sizeof(AtomicCounterData) * num_cores_));
114 CallCountingHelper::~CallCountingHelper() {
115 gpr_free(per_cpu_counter_data_storage_);
118 void CallCountingHelper::RecordCallStarted() {
119 gpr_atm_no_barrier_fetch_add(
120 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
122 static_cast<gpr_atm>(1));
123 gpr_atm_no_barrier_store(
124 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
125 .last_call_started_millis,
126 (gpr_atm)ExecCtx::Get()->Now());
129 void CallCountingHelper::RecordCallFailed() {
130 gpr_atm_no_barrier_fetch_add(
131 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
133 static_cast<gpr_atm>(1));
136 void CallCountingHelper::RecordCallSucceeded() {
137 gpr_atm_no_barrier_fetch_add(
138 &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
140 static_cast<gpr_atm>(1));
143 void CallCountingHelper::CollectData(CounterData* out) {
144 for (size_t core = 0; core < num_cores_; ++core) {
145 out->calls_started += gpr_atm_no_barrier_load(
146 &per_cpu_counter_data_storage_[core].calls_started);
147 out->calls_succeeded += gpr_atm_no_barrier_load(
148 &per_cpu_counter_data_storage_[core].calls_succeeded);
149 out->calls_failed += gpr_atm_no_barrier_load(
150 &per_cpu_counter_data_storage_[core].calls_failed);
151 gpr_atm last_call = gpr_atm_no_barrier_load(
152 &per_cpu_counter_data_storage_[core].last_call_started_millis);
153 if (last_call > out->last_call_started_millis) {
154 out->last_call_started_millis = last_call;
159 void CallCountingHelper::PopulateCallCounts(grpc_json* json) {
160 grpc_json* json_iterator = nullptr;
163 if (data.calls_started != 0) {
164 json_iterator = grpc_json_add_number_string_child(
165 json, json_iterator, "callsStarted", data.calls_started);
167 if (data.calls_succeeded != 0) {
168 json_iterator = grpc_json_add_number_string_child(
169 json, json_iterator, "callsSucceeded", data.calls_succeeded);
171 if (data.calls_failed) {
172 json_iterator = grpc_json_add_number_string_child(
173 json, json_iterator, "callsFailed", data.calls_failed);
175 if (data.calls_started != 0) {
176 gpr_timespec ts = grpc_millis_to_timespec(data.last_call_started_millis,
179 grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
180 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
188 ChannelNode::ChannelNode(UniquePtr<char> target,
189 size_t channel_tracer_max_nodes, intptr_t parent_uuid)
190 : BaseNode(parent_uuid == 0 ? EntityType::kTopLevelChannel
191 : EntityType::kInternalChannel,
192 UniquePtr<char>(gpr_strdup(target.get()))),
193 target_(std::move(target)),
194 trace_(channel_tracer_max_nodes),
195 parent_uuid_(parent_uuid) {}
197 const char* ChannelNode::GetChannelConnectivityStateChangeString(
198 grpc_connectivity_state state) {
200 case GRPC_CHANNEL_IDLE:
201 return "Channel state change to IDLE";
202 case GRPC_CHANNEL_CONNECTING:
203 return "Channel state change to CONNECTING";
204 case GRPC_CHANNEL_READY:
205 return "Channel state change to READY";
206 case GRPC_CHANNEL_TRANSIENT_FAILURE:
207 return "Channel state change to TRANSIENT_FAILURE";
208 case GRPC_CHANNEL_SHUTDOWN:
209 return "Channel state change to SHUTDOWN";
211 GPR_UNREACHABLE_CODE(return "UNKNOWN");
214 grpc_json* ChannelNode::RenderJson() {
215 // We need to track these three json objects to build our object
216 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
217 grpc_json* json = top_level_json;
218 grpc_json* json_iterator = nullptr;
219 // create and fill the ref child
220 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
221 GRPC_JSON_OBJECT, false);
222 json = json_iterator;
223 json_iterator = nullptr;
224 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
225 "channelId", uuid());
226 // reset json iterators to top level object
227 json = top_level_json;
228 json_iterator = nullptr;
229 // create and fill the data child.
230 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
231 GRPC_JSON_OBJECT, false);
233 json_iterator = nullptr;
234 // connectivity state
235 // If low-order bit is on, then the field is set.
236 int state_field = connectivity_state_.Load(MemoryOrder::RELAXED);
237 if ((state_field & 1) != 0) {
238 grpc_connectivity_state state =
239 static_cast<grpc_connectivity_state>(state_field >> 1);
240 json = grpc_json_create_child(nullptr, json, "state", nullptr,
241 GRPC_JSON_OBJECT, false);
242 grpc_json_create_child(nullptr, json, "state",
243 grpc_connectivity_state_name(state),
244 GRPC_JSON_STRING, false);
247 // populate the target.
248 GPR_ASSERT(target_.get() != nullptr);
249 grpc_json_create_child(nullptr, json, "target", target_.get(),
250 GRPC_JSON_STRING, false);
251 // fill in the channel trace if applicable
252 grpc_json* trace_json = trace_.RenderJson();
253 if (trace_json != nullptr) {
254 trace_json->key = "trace"; // this object is named trace in channelz.proto
255 grpc_json_link_child(json, trace_json, nullptr);
257 // ask CallCountingHelper to populate trace and call count data.
258 call_counter_.PopulateCallCounts(json);
259 json = top_level_json;
260 // template method. Child classes may override this to add their specific
262 PopulateChildRefs(json);
263 return top_level_json;
266 void ChannelNode::PopulateChildRefs(grpc_json* json) {
267 MutexLock lock(&child_mu_);
268 grpc_json* json_iterator = nullptr;
269 if (!child_subchannels_.empty()) {
270 grpc_json* array_parent = grpc_json_create_child(
271 nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false);
272 for (const auto& p : child_subchannels_) {
274 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
275 GRPC_JSON_OBJECT, false);
276 grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId",
280 if (!child_channels_.empty()) {
281 grpc_json* array_parent = grpc_json_create_child(
282 nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false);
283 json_iterator = nullptr;
284 for (const auto& p : child_channels_) {
286 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
287 GRPC_JSON_OBJECT, false);
288 grpc_json_add_number_string_child(json_iterator, nullptr, "channelId",
294 void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
295 // Store with low-order bit set to indicate that the field is set.
296 int state_field = (state << 1) + 1;
297 connectivity_state_.Store(state_field, MemoryOrder::RELAXED);
300 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
301 MutexLock lock(&child_mu_);
302 child_channels_.insert(MakePair(child_uuid, true));
305 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
306 MutexLock lock(&child_mu_);
307 child_channels_.erase(child_uuid);
310 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
311 MutexLock lock(&child_mu_);
312 child_subchannels_.insert(MakePair(child_uuid, true));
315 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
316 MutexLock lock(&child_mu_);
317 child_subchannels_.erase(child_uuid);
324 ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes)
325 : BaseNode(EntityType::kServer, /* name */ nullptr),
326 trace_(channel_tracer_max_nodes) {}
328 ServerNode::~ServerNode() {}
330 void ServerNode::AddChildSocket(RefCountedPtr<SocketNode> node) {
331 MutexLock lock(&child_mu_);
332 child_sockets_.insert(MakePair(node->uuid(), std::move(node)));
335 void ServerNode::RemoveChildSocket(intptr_t child_uuid) {
336 MutexLock lock(&child_mu_);
337 child_sockets_.erase(child_uuid);
340 void ServerNode::AddChildListenSocket(RefCountedPtr<ListenSocketNode> node) {
341 MutexLock lock(&child_mu_);
342 child_listen_sockets_.insert(MakePair(node->uuid(), std::move(node)));
345 void ServerNode::RemoveChildListenSocket(intptr_t child_uuid) {
346 MutexLock lock(&child_mu_);
347 child_listen_sockets_.erase(child_uuid);
350 char* ServerNode::RenderServerSockets(intptr_t start_socket_id,
351 intptr_t max_results) {
352 // If user does not set max_results, we choose 500.
353 size_t pagination_limit = max_results == 0 ? 500 : max_results;
354 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
355 grpc_json* json = top_level_json;
356 grpc_json* json_iterator = nullptr;
357 MutexLock lock(&child_mu_);
358 size_t sockets_rendered = 0;
359 if (!child_sockets_.empty()) {
360 // Create list of socket refs
361 grpc_json* array_parent = grpc_json_create_child(
362 nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
363 const size_t limit = GPR_MIN(child_sockets_.size(), pagination_limit);
364 for (auto it = child_sockets_.lower_bound(start_socket_id);
365 it != child_sockets_.end() && sockets_rendered < limit;
366 ++it, ++sockets_rendered) {
367 grpc_json* socket_ref_json = grpc_json_create_child(
368 nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false);
369 json_iterator = grpc_json_add_number_string_child(
370 socket_ref_json, nullptr, "socketId", it->first);
371 grpc_json_create_child(json_iterator, socket_ref_json, "name",
372 it->second->name(), GRPC_JSON_STRING, false);
375 if (sockets_rendered == child_sockets_.size()) {
376 json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
377 GRPC_JSON_TRUE, false);
379 char* json_str = grpc_json_dump_to_string(top_level_json, 0);
380 grpc_json_destroy(top_level_json);
384 grpc_json* ServerNode::RenderJson() {
385 // We need to track these three json objects to build our object
386 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
387 grpc_json* json = top_level_json;
388 grpc_json* json_iterator = nullptr;
389 // create and fill the ref child
390 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
391 GRPC_JSON_OBJECT, false);
392 json = json_iterator;
393 json_iterator = nullptr;
394 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
396 // reset json iterators to top level object
397 json = top_level_json;
398 json_iterator = nullptr;
399 // create and fill the data child.
400 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
401 GRPC_JSON_OBJECT, false);
403 json_iterator = nullptr;
404 // fill in the channel trace if applicable
405 grpc_json* trace_json = trace_.RenderJson();
406 if (trace_json != nullptr) {
407 trace_json->key = "trace"; // this object is named trace in channelz.proto
408 grpc_json_link_child(json, trace_json, nullptr);
410 // ask CallCountingHelper to populate trace and call count data.
411 call_counter_.PopulateCallCounts(json);
412 json = top_level_json;
413 // Render listen sockets
414 MutexLock lock(&child_mu_);
415 if (!child_listen_sockets_.empty()) {
416 grpc_json* array_parent = grpc_json_create_child(
417 nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false);
418 for (const auto& it : child_listen_sockets_) {
420 grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
421 GRPC_JSON_OBJECT, false);
422 grpc_json* sibling_iterator = grpc_json_add_number_string_child(
423 json_iterator, nullptr, "socketId", it.first);
424 grpc_json_create_child(sibling_iterator, json_iterator, "name",
425 it.second->name(), GRPC_JSON_STRING, false);
428 return top_level_json;
437 void PopulateSocketAddressJson(grpc_json* json, const char* name,
438 const char* addr_str) {
439 if (addr_str == nullptr) return;
440 grpc_json* json_iterator = nullptr;
441 json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr,
442 GRPC_JSON_OBJECT, false);
443 json = json_iterator;
444 json_iterator = nullptr;
445 grpc_uri* uri = grpc_uri_parse(addr_str, true);
446 if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) ||
447 (strcmp(uri->scheme, "ipv6") == 0))) {
448 const char* host_port = uri->path;
449 if (*host_port == '/') ++host_port;
450 UniquePtr<char> host;
451 UniquePtr<char> port;
452 GPR_ASSERT(SplitHostPort(host_port, &host, &port));
454 if (port != nullptr) {
455 port_num = atoi(port.get());
458 grpc_base64_encode(host.get(), strlen(host.get()), false, false);
459 json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address",
460 nullptr, GRPC_JSON_OBJECT, false);
461 json = json_iterator;
462 json_iterator = nullptr;
463 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
465 json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
466 b64_host, GRPC_JSON_STRING, true);
467 } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) {
468 json_iterator = grpc_json_create_child(json_iterator, json, "uds_address",
469 nullptr, GRPC_JSON_OBJECT, false);
470 json = json_iterator;
471 json_iterator = nullptr;
473 grpc_json_create_child(json_iterator, json, "filename",
474 gpr_strdup(uri->path), GRPC_JSON_STRING, true);
476 json_iterator = grpc_json_create_child(json_iterator, json, "other_address",
477 nullptr, GRPC_JSON_OBJECT, false);
478 json = json_iterator;
479 json_iterator = nullptr;
480 json_iterator = grpc_json_create_child(json_iterator, json, "name",
481 addr_str, GRPC_JSON_STRING, false);
483 grpc_uri_destroy(uri);
488 SocketNode::SocketNode(UniquePtr<char> local, UniquePtr<char> remote,
489 UniquePtr<char> name)
490 : BaseNode(EntityType::kSocket, std::move(name)),
491 local_(std::move(local)),
492 remote_(std::move(remote)) {}
494 void SocketNode::RecordStreamStartedFromLocal() {
495 gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
496 gpr_atm_no_barrier_store(&last_local_stream_created_millis_,
497 (gpr_atm)ExecCtx::Get()->Now());
500 void SocketNode::RecordStreamStartedFromRemote() {
501 gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
502 gpr_atm_no_barrier_store(&last_remote_stream_created_millis_,
503 (gpr_atm)ExecCtx::Get()->Now());
506 void SocketNode::RecordMessagesSent(uint32_t num_sent) {
507 gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast<gpr_atm>(num_sent));
508 gpr_atm_no_barrier_store(&last_message_sent_millis_,
509 (gpr_atm)ExecCtx::Get()->Now());
512 void SocketNode::RecordMessageReceived() {
513 gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast<gpr_atm>(1));
514 gpr_atm_no_barrier_store(&last_message_received_millis_,
515 (gpr_atm)ExecCtx::Get()->Now());
518 grpc_json* SocketNode::RenderJson() {
519 // We need to track these three json objects to build our object
520 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
521 grpc_json* json = top_level_json;
522 grpc_json* json_iterator = nullptr;
523 // create and fill the ref child
524 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
525 GRPC_JSON_OBJECT, false);
526 json = json_iterator;
527 json_iterator = nullptr;
528 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
530 json_iterator = grpc_json_create_child(json_iterator, json, "name", name(),
531 GRPC_JSON_STRING, false);
532 json = top_level_json;
533 PopulateSocketAddressJson(json, "remote", remote_.get());
534 PopulateSocketAddressJson(json, "local", local_.get());
535 // reset json iterators to top level object
536 json = top_level_json;
537 json_iterator = nullptr;
538 // create and fill the data child.
539 grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
540 GRPC_JSON_OBJECT, false);
542 json_iterator = nullptr;
544 gpr_atm streams_started = gpr_atm_no_barrier_load(&streams_started_);
545 if (streams_started != 0) {
546 json_iterator = grpc_json_add_number_string_child(
547 json, json_iterator, "streamsStarted", streams_started);
548 gpr_atm last_local_stream_created_millis =
549 gpr_atm_no_barrier_load(&last_local_stream_created_millis_);
550 if (last_local_stream_created_millis != 0) {
551 ts = grpc_millis_to_timespec(last_local_stream_created_millis,
553 json_iterator = grpc_json_create_child(
554 json_iterator, json, "lastLocalStreamCreatedTimestamp",
555 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
557 gpr_atm last_remote_stream_created_millis =
558 gpr_atm_no_barrier_load(&last_remote_stream_created_millis_);
559 if (last_remote_stream_created_millis != 0) {
560 ts = grpc_millis_to_timespec(last_remote_stream_created_millis,
562 json_iterator = grpc_json_create_child(
563 json_iterator, json, "lastRemoteStreamCreatedTimestamp",
564 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
567 gpr_atm streams_succeeded = gpr_atm_no_barrier_load(&streams_succeeded_);
568 if (streams_succeeded != 0) {
569 json_iterator = grpc_json_add_number_string_child(
570 json, json_iterator, "streamsSucceeded", streams_succeeded);
572 gpr_atm streams_failed = gpr_atm_no_barrier_load(&streams_failed_);
573 if (streams_failed) {
574 json_iterator = grpc_json_add_number_string_child(
575 json, json_iterator, "streamsFailed", streams_failed);
577 gpr_atm messages_sent = gpr_atm_no_barrier_load(&messages_sent_);
578 if (messages_sent != 0) {
579 json_iterator = grpc_json_add_number_string_child(
580 json, json_iterator, "messagesSent", messages_sent);
581 ts = grpc_millis_to_timespec(
582 gpr_atm_no_barrier_load(&last_message_sent_millis_),
585 grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp",
586 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
588 gpr_atm messages_received = gpr_atm_no_barrier_load(&messages_received_);
589 if (messages_received != 0) {
590 json_iterator = grpc_json_add_number_string_child(
591 json, json_iterator, "messagesReceived", messages_received);
592 ts = grpc_millis_to_timespec(
593 gpr_atm_no_barrier_load(&last_message_received_millis_),
595 json_iterator = grpc_json_create_child(
596 json_iterator, json, "lastMessageReceivedTimestamp",
597 gpr_format_timespec(ts), GRPC_JSON_STRING, true);
599 gpr_atm keepalives_sent = gpr_atm_no_barrier_load(&keepalives_sent_);
600 if (keepalives_sent != 0) {
601 json_iterator = grpc_json_add_number_string_child(
602 json, json_iterator, "keepAlivesSent", keepalives_sent);
604 return top_level_json;
611 ListenSocketNode::ListenSocketNode(UniquePtr<char> local_addr,
612 UniquePtr<char> name)
613 : BaseNode(EntityType::kSocket, std::move(name)),
614 local_addr_(std::move(local_addr)) {}
616 grpc_json* ListenSocketNode::RenderJson() {
617 // We need to track these three json objects to build our object
618 grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
619 grpc_json* json = top_level_json;
620 grpc_json* json_iterator = nullptr;
621 // create and fill the ref child
622 json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
623 GRPC_JSON_OBJECT, false);
624 json = json_iterator;
625 json_iterator = nullptr;
626 json_iterator = grpc_json_add_number_string_child(json, json_iterator,
628 json_iterator = grpc_json_create_child(json_iterator, json, "name", name(),
629 GRPC_JSON_STRING, false);
630 json = top_level_json;
631 PopulateSocketAddressJson(json, "local", local_addr_.get());
633 return top_level_json;
636 } // namespace channelz
637 } // namespace grpc_core