Imported Upstream version 1.22.0
[platform/upstream/grpc.git] / src / core / lib / channel / channelz.cc
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 <grpc/grpc.h>
24 #include <grpc/support/alloc.h>
25 #include <grpc/support/log.h>
26 #include <grpc/support/string_util.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "src/core/lib/channel/channelz_registry.h"
32 #include "src/core/lib/channel/status_util.h"
33 #include "src/core/lib/gpr/host_port.h"
34 #include "src/core/lib/gpr/string.h"
35 #include "src/core/lib/gpr/useful.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"
46
47 namespace grpc_core {
48 namespace channelz {
49
50 //
51 // channel arg code
52 //
53
54 namespace {
55
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};
61
62 }  // namespace
63
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);
75 }
76
77 intptr_t GetParentUuidFromArgs(const grpc_channel_args& args) {
78   const grpc_arg* arg =
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);
82 }
83
84 //
85 // BaseNode
86 //
87
88 BaseNode::BaseNode(EntityType type) : type_(type), uuid_(-1) {
89   // The registry will set uuid_ under its lock.
90   ChannelzRegistry::Register(this);
91 }
92
93 BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
94
95 char* BaseNode::RenderJsonString() {
96   grpc_json* json = RenderJson();
97   GPR_ASSERT(json != nullptr);
98   char* json_str = grpc_json_dump_to_string(json, 0);
99   grpc_json_destroy(json);
100   return json_str;
101 }
102
103 //
104 // CallCountingHelper
105 //
106
107 CallCountingHelper::CallCountingHelper() {
108   num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
109   per_cpu_counter_data_storage_ = static_cast<AtomicCounterData*>(
110       gpr_zalloc(sizeof(AtomicCounterData) * num_cores_));
111 }
112
113 CallCountingHelper::~CallCountingHelper() {
114   gpr_free(per_cpu_counter_data_storage_);
115 }
116
117 void CallCountingHelper::RecordCallStarted() {
118   gpr_atm_no_barrier_fetch_add(
119       &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
120            .calls_started,
121       static_cast<gpr_atm>(1));
122   gpr_atm_no_barrier_store(
123       &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
124            .last_call_started_millis,
125       (gpr_atm)ExecCtx::Get()->Now());
126 }
127
128 void CallCountingHelper::RecordCallFailed() {
129   gpr_atm_no_barrier_fetch_add(
130       &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
131            .calls_failed,
132       static_cast<gpr_atm>(1));
133 }
134
135 void CallCountingHelper::RecordCallSucceeded() {
136   gpr_atm_no_barrier_fetch_add(
137       &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
138            .calls_succeeded,
139       static_cast<gpr_atm>(1));
140 }
141
142 void CallCountingHelper::CollectData(CounterData* out) {
143   for (size_t core = 0; core < num_cores_; ++core) {
144     out->calls_started += gpr_atm_no_barrier_load(
145         &per_cpu_counter_data_storage_[core].calls_started);
146     out->calls_succeeded += gpr_atm_no_barrier_load(
147         &per_cpu_counter_data_storage_[core].calls_succeeded);
148     out->calls_failed += gpr_atm_no_barrier_load(
149         &per_cpu_counter_data_storage_[core].calls_failed);
150     gpr_atm last_call = gpr_atm_no_barrier_load(
151         &per_cpu_counter_data_storage_[core].last_call_started_millis);
152     if (last_call > out->last_call_started_millis) {
153       out->last_call_started_millis = last_call;
154     }
155   }
156 }
157
158 void CallCountingHelper::PopulateCallCounts(grpc_json* json) {
159   grpc_json* json_iterator = nullptr;
160   CounterData data;
161   CollectData(&data);
162   if (data.calls_started != 0) {
163     json_iterator = grpc_json_add_number_string_child(
164         json, json_iterator, "callsStarted", data.calls_started);
165   }
166   if (data.calls_succeeded != 0) {
167     json_iterator = grpc_json_add_number_string_child(
168         json, json_iterator, "callsSucceeded", data.calls_succeeded);
169   }
170   if (data.calls_failed) {
171     json_iterator = grpc_json_add_number_string_child(
172         json, json_iterator, "callsFailed", data.calls_failed);
173   }
174   if (data.calls_started != 0) {
175     gpr_timespec ts = grpc_millis_to_timespec(data.last_call_started_millis,
176                                               GPR_CLOCK_REALTIME);
177     json_iterator =
178         grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
179                                gpr_format_timespec(ts), GRPC_JSON_STRING, true);
180   }
181 }
182
183 //
184 // ChannelNode
185 //
186
187 ChannelNode::ChannelNode(UniquePtr<char> target,
188                          size_t channel_tracer_max_nodes, intptr_t parent_uuid)
189     : BaseNode(parent_uuid == 0 ? EntityType::kTopLevelChannel
190                                 : EntityType::kInternalChannel),
191       target_(std::move(target)),
192       trace_(channel_tracer_max_nodes),
193       parent_uuid_(parent_uuid) {}
194
195 grpc_json* ChannelNode::RenderJson() {
196   // We need to track these three json objects to build our object
197   grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
198   grpc_json* json = top_level_json;
199   grpc_json* json_iterator = nullptr;
200   // create and fill the ref child
201   json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
202                                          GRPC_JSON_OBJECT, false);
203   json = json_iterator;
204   json_iterator = nullptr;
205   json_iterator = grpc_json_add_number_string_child(json, json_iterator,
206                                                     "channelId", uuid());
207   // reset json iterators to top level object
208   json = top_level_json;
209   json_iterator = nullptr;
210   // create and fill the data child.
211   grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
212                                            GRPC_JSON_OBJECT, false);
213   json = data;
214   json_iterator = nullptr;
215   // connectivity state
216   // If low-order bit is on, then the field is set.
217   int state_field = connectivity_state_.Load(MemoryOrder::RELAXED);
218   if ((state_field & 1) != 0) {
219     grpc_connectivity_state state =
220         static_cast<grpc_connectivity_state>(state_field >> 1);
221     json = grpc_json_create_child(nullptr, json, "state", nullptr,
222                                   GRPC_JSON_OBJECT, false);
223     grpc_json_create_child(nullptr, json, "state",
224                            grpc_connectivity_state_name(state),
225                            GRPC_JSON_STRING, false);
226     json = data;
227   }
228   // populate the target.
229   GPR_ASSERT(target_.get() != nullptr);
230   grpc_json_create_child(nullptr, json, "target", target_.get(),
231                          GRPC_JSON_STRING, false);
232   // fill in the channel trace if applicable
233   grpc_json* trace_json = trace_.RenderJson();
234   if (trace_json != nullptr) {
235     trace_json->key = "trace";  // this object is named trace in channelz.proto
236     grpc_json_link_child(json, trace_json, nullptr);
237   }
238   // ask CallCountingHelper to populate trace and call count data.
239   call_counter_.PopulateCallCounts(json);
240   json = top_level_json;
241   // template method. Child classes may override this to add their specific
242   // functionality.
243   PopulateChildRefs(json);
244   return top_level_json;
245 }
246
247 void ChannelNode::PopulateChildRefs(grpc_json* json) {
248   MutexLock lock(&child_mu_);
249   grpc_json* json_iterator = nullptr;
250   if (!child_subchannels_.empty()) {
251     grpc_json* array_parent = grpc_json_create_child(
252         nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false);
253     for (const auto& p : child_subchannels_) {
254       json_iterator =
255           grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
256                                  GRPC_JSON_OBJECT, false);
257       grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId",
258                                         p.first);
259     }
260   }
261   if (!child_channels_.empty()) {
262     grpc_json* array_parent = grpc_json_create_child(
263         nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false);
264     json_iterator = nullptr;
265     for (const auto& p : child_channels_) {
266       json_iterator =
267           grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
268                                  GRPC_JSON_OBJECT, false);
269       grpc_json_add_number_string_child(json_iterator, nullptr, "channelId",
270                                         p.first);
271     }
272   }
273 }
274
275 void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
276   // Store with low-order bit set to indicate that the field is set.
277   int state_field = (state << 1) + 1;
278   connectivity_state_.Store(state_field, MemoryOrder::RELAXED);
279 }
280
281 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
282   MutexLock lock(&child_mu_);
283   child_channels_.insert(MakePair(child_uuid, true));
284 }
285
286 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
287   MutexLock lock(&child_mu_);
288   child_channels_.erase(child_uuid);
289 }
290
291 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
292   MutexLock lock(&child_mu_);
293   child_subchannels_.insert(MakePair(child_uuid, true));
294 }
295
296 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
297   MutexLock lock(&child_mu_);
298   child_subchannels_.erase(child_uuid);
299 }
300
301 //
302 // ServerNode
303 //
304
305 ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes)
306     : BaseNode(EntityType::kServer),
307       server_(server),
308       trace_(channel_tracer_max_nodes) {}
309
310 ServerNode::~ServerNode() {}
311
312 char* ServerNode::RenderServerSockets(intptr_t start_socket_id,
313                                       intptr_t max_results) {
314   // if user does not set max_results, we choose 500.
315   size_t pagination_limit = max_results == 0 ? 500 : max_results;
316   grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
317   grpc_json* json = top_level_json;
318   grpc_json* json_iterator = nullptr;
319   ChildSocketsList socket_refs;
320   grpc_server_populate_server_sockets(server_, &socket_refs, start_socket_id);
321   // declared early so it can be used outside of the loop.
322   size_t i = 0;
323   if (!socket_refs.empty()) {
324     // create list of socket refs
325     grpc_json* array_parent = grpc_json_create_child(
326         nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
327     for (i = 0; i < GPR_MIN(socket_refs.size(), pagination_limit); ++i) {
328       grpc_json* socket_ref_json = grpc_json_create_child(
329           nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false);
330       json_iterator = grpc_json_add_number_string_child(
331           socket_ref_json, nullptr, "socketId", socket_refs[i]->uuid());
332       grpc_json_create_child(json_iterator, socket_ref_json, "name",
333                              socket_refs[i]->remote(), GRPC_JSON_STRING, false);
334     }
335   }
336   if (i == socket_refs.size()) {
337     json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
338                                            GRPC_JSON_TRUE, false);
339   }
340   char* json_str = grpc_json_dump_to_string(top_level_json, 0);
341   grpc_json_destroy(top_level_json);
342   return json_str;
343 }
344
345 grpc_json* ServerNode::RenderJson() {
346   // We need to track these three json objects to build our object
347   grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
348   grpc_json* json = top_level_json;
349   grpc_json* json_iterator = nullptr;
350   // create and fill the ref child
351   json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
352                                          GRPC_JSON_OBJECT, false);
353   json = json_iterator;
354   json_iterator = nullptr;
355   json_iterator = grpc_json_add_number_string_child(json, json_iterator,
356                                                     "serverId", uuid());
357   // reset json iterators to top level object
358   json = top_level_json;
359   json_iterator = nullptr;
360   // create and fill the data child.
361   grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
362                                            GRPC_JSON_OBJECT, false);
363   json = data;
364   json_iterator = nullptr;
365   // fill in the channel trace if applicable
366   grpc_json* trace_json = trace_.RenderJson();
367   if (trace_json != nullptr) {
368     trace_json->key = "trace";  // this object is named trace in channelz.proto
369     grpc_json_link_child(json, trace_json, nullptr);
370   }
371   // ask CallCountingHelper to populate trace and call count data.
372   call_counter_.PopulateCallCounts(json);
373   json = top_level_json;
374   ChildRefsList listen_sockets;
375   grpc_server_populate_listen_sockets(server_, &listen_sockets);
376   if (!listen_sockets.empty()) {
377     grpc_json* array_parent = grpc_json_create_child(
378         nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false);
379     for (size_t i = 0; i < listen_sockets.size(); ++i) {
380       json_iterator =
381           grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
382                                  GRPC_JSON_OBJECT, false);
383       grpc_json_add_number_string_child(json_iterator, nullptr, "socketId",
384                                         listen_sockets[i]);
385     }
386   }
387   return top_level_json;
388 }
389
390 //
391 // SocketNode
392 //
393
394 namespace {
395
396 void PopulateSocketAddressJson(grpc_json* json, const char* name,
397                                const char* addr_str) {
398   if (addr_str == nullptr) return;
399   grpc_json* json_iterator = nullptr;
400   json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr,
401                                          GRPC_JSON_OBJECT, false);
402   json = json_iterator;
403   json_iterator = nullptr;
404   grpc_uri* uri = grpc_uri_parse(addr_str, true);
405   if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) ||
406                            (strcmp(uri->scheme, "ipv6") == 0))) {
407     const char* host_port = uri->path;
408     if (*host_port == '/') ++host_port;
409     char* host = nullptr;
410     char* port = nullptr;
411     GPR_ASSERT(gpr_split_host_port(host_port, &host, &port));
412     int port_num = -1;
413     if (port != nullptr) {
414       port_num = atoi(port);
415     }
416     char* b64_host = grpc_base64_encode(host, strlen(host), false, false);
417     json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address",
418                                            nullptr, GRPC_JSON_OBJECT, false);
419     json = json_iterator;
420     json_iterator = nullptr;
421     json_iterator = grpc_json_add_number_string_child(json, json_iterator,
422                                                       "port", port_num);
423     json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
424                                            b64_host, GRPC_JSON_STRING, true);
425     gpr_free(host);
426     gpr_free(port);
427   } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) {
428     json_iterator = grpc_json_create_child(json_iterator, json, "uds_address",
429                                            nullptr, GRPC_JSON_OBJECT, false);
430     json = json_iterator;
431     json_iterator = nullptr;
432     json_iterator =
433         grpc_json_create_child(json_iterator, json, "filename",
434                                gpr_strdup(uri->path), GRPC_JSON_STRING, true);
435   } else {
436     json_iterator = grpc_json_create_child(json_iterator, json, "other_address",
437                                            nullptr, GRPC_JSON_OBJECT, false);
438     json = json_iterator;
439     json_iterator = nullptr;
440     json_iterator = grpc_json_create_child(json_iterator, json, "name",
441                                            addr_str, GRPC_JSON_STRING, false);
442   }
443   grpc_uri_destroy(uri);
444 }
445
446 }  // namespace
447
448 SocketNode::SocketNode(UniquePtr<char> local, UniquePtr<char> remote)
449     : BaseNode(EntityType::kSocket),
450       local_(std::move(local)),
451       remote_(std::move(remote)) {}
452
453 void SocketNode::RecordStreamStartedFromLocal() {
454   gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
455   gpr_atm_no_barrier_store(&last_local_stream_created_millis_,
456                            (gpr_atm)ExecCtx::Get()->Now());
457 }
458
459 void SocketNode::RecordStreamStartedFromRemote() {
460   gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
461   gpr_atm_no_barrier_store(&last_remote_stream_created_millis_,
462                            (gpr_atm)ExecCtx::Get()->Now());
463 }
464
465 void SocketNode::RecordMessagesSent(uint32_t num_sent) {
466   gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast<gpr_atm>(num_sent));
467   gpr_atm_no_barrier_store(&last_message_sent_millis_,
468                            (gpr_atm)ExecCtx::Get()->Now());
469 }
470
471 void SocketNode::RecordMessageReceived() {
472   gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast<gpr_atm>(1));
473   gpr_atm_no_barrier_store(&last_message_received_millis_,
474                            (gpr_atm)ExecCtx::Get()->Now());
475 }
476
477 grpc_json* SocketNode::RenderJson() {
478   // We need to track these three json objects to build our object
479   grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
480   grpc_json* json = top_level_json;
481   grpc_json* json_iterator = nullptr;
482   // create and fill the ref child
483   json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
484                                          GRPC_JSON_OBJECT, false);
485   json = json_iterator;
486   json_iterator = nullptr;
487   json_iterator = grpc_json_add_number_string_child(json, json_iterator,
488                                                     "socketId", uuid());
489   json = top_level_json;
490   PopulateSocketAddressJson(json, "remote", remote_.get());
491   PopulateSocketAddressJson(json, "local", local_.get());
492   // reset json iterators to top level object
493   json = top_level_json;
494   json_iterator = nullptr;
495   // create and fill the data child.
496   grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
497                                            GRPC_JSON_OBJECT, false);
498   json = data;
499   json_iterator = nullptr;
500   gpr_timespec ts;
501   gpr_atm streams_started = gpr_atm_no_barrier_load(&streams_started_);
502   if (streams_started != 0) {
503     json_iterator = grpc_json_add_number_string_child(
504         json, json_iterator, "streamsStarted", streams_started);
505     gpr_atm last_local_stream_created_millis =
506         gpr_atm_no_barrier_load(&last_local_stream_created_millis_);
507     if (last_local_stream_created_millis != 0) {
508       ts = grpc_millis_to_timespec(last_local_stream_created_millis,
509                                    GPR_CLOCK_REALTIME);
510       json_iterator = grpc_json_create_child(
511           json_iterator, json, "lastLocalStreamCreatedTimestamp",
512           gpr_format_timespec(ts), GRPC_JSON_STRING, true);
513     }
514     gpr_atm last_remote_stream_created_millis =
515         gpr_atm_no_barrier_load(&last_remote_stream_created_millis_);
516     if (last_remote_stream_created_millis != 0) {
517       ts = grpc_millis_to_timespec(last_remote_stream_created_millis,
518                                    GPR_CLOCK_REALTIME);
519       json_iterator = grpc_json_create_child(
520           json_iterator, json, "lastRemoteStreamCreatedTimestamp",
521           gpr_format_timespec(ts), GRPC_JSON_STRING, true);
522     }
523   }
524   gpr_atm streams_succeeded = gpr_atm_no_barrier_load(&streams_succeeded_);
525   if (streams_succeeded != 0) {
526     json_iterator = grpc_json_add_number_string_child(
527         json, json_iterator, "streamsSucceeded", streams_succeeded);
528   }
529   gpr_atm streams_failed = gpr_atm_no_barrier_load(&streams_failed_);
530   if (streams_failed) {
531     json_iterator = grpc_json_add_number_string_child(
532         json, json_iterator, "streamsFailed", streams_failed);
533   }
534   gpr_atm messages_sent = gpr_atm_no_barrier_load(&messages_sent_);
535   if (messages_sent != 0) {
536     json_iterator = grpc_json_add_number_string_child(
537         json, json_iterator, "messagesSent", messages_sent);
538     ts = grpc_millis_to_timespec(
539         gpr_atm_no_barrier_load(&last_message_sent_millis_),
540         GPR_CLOCK_REALTIME);
541     json_iterator =
542         grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp",
543                                gpr_format_timespec(ts), GRPC_JSON_STRING, true);
544   }
545   gpr_atm messages_received = gpr_atm_no_barrier_load(&messages_received_);
546   if (messages_received != 0) {
547     json_iterator = grpc_json_add_number_string_child(
548         json, json_iterator, "messagesReceived", messages_received);
549     ts = grpc_millis_to_timespec(
550         gpr_atm_no_barrier_load(&last_message_received_millis_),
551         GPR_CLOCK_REALTIME);
552     json_iterator = grpc_json_create_child(
553         json_iterator, json, "lastMessageReceivedTimestamp",
554         gpr_format_timespec(ts), GRPC_JSON_STRING, true);
555   }
556   gpr_atm keepalives_sent = gpr_atm_no_barrier_load(&keepalives_sent_);
557   if (keepalives_sent != 0) {
558     json_iterator = grpc_json_add_number_string_child(
559         json, json_iterator, "keepAlivesSent", keepalives_sent);
560   }
561   return top_level_json;
562 }
563
564 //
565 // ListenSocketNode
566 //
567
568 ListenSocketNode::ListenSocketNode(UniquePtr<char> local_addr)
569     : BaseNode(EntityType::kSocket), local_addr_(std::move(local_addr)) {}
570
571 grpc_json* ListenSocketNode::RenderJson() {
572   // We need to track these three json objects to build our object
573   grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
574   grpc_json* json = top_level_json;
575   grpc_json* json_iterator = nullptr;
576   // create and fill the ref child
577   json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
578                                          GRPC_JSON_OBJECT, false);
579   json = json_iterator;
580   json_iterator = nullptr;
581   json_iterator = grpc_json_add_number_string_child(json, json_iterator,
582                                                     "socketId", uuid());
583   json = top_level_json;
584   PopulateSocketAddressJson(json, "local", local_addr_.get());
585
586   return top_level_json;
587 }
588
589 }  // namespace channelz
590 }  // namespace grpc_core