Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc / deps / grpc / src / core / lib / channel / channelz.cc
diff --git a/legacy-libs/grpc/deps/grpc/src/core/lib/channel/channelz.cc b/legacy-libs/grpc/deps/grpc/src/core/lib/channel/channelz.cc
new file mode 100644 (file)
index 0000000..fb913be
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ *
+ * Copyright 2017 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/impl/codegen/port_platform.h>
+
+#include "src/core/lib/channel/channelz.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/core/lib/channel/channelz_registry.h"
+#include "src/core/lib/channel/status_util.h"
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/useful.h"
+#include "src/core/lib/gprpp/host_port.h"
+#include "src/core/lib/gprpp/memory.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/slice/b64.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/server.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/error_utils.h"
+#include "src/core/lib/uri/uri_parser.h"
+
+namespace grpc_core {
+namespace channelz {
+
+//
+// channel arg code
+//
+
+namespace {
+
+void* parent_uuid_copy(void* p) { return p; }
+void parent_uuid_destroy(void* p) {}
+int parent_uuid_cmp(void* p1, void* p2) { return GPR_ICMP(p1, p2); }
+const grpc_arg_pointer_vtable parent_uuid_vtable = {
+    parent_uuid_copy, parent_uuid_destroy, parent_uuid_cmp};
+
+}  // namespace
+
+grpc_arg MakeParentUuidArg(intptr_t parent_uuid) {
+  // We would ideally like to store the uuid in an integer argument.
+  // Unfortunately, that won't work, because intptr_t (the type used for
+  // uuids) doesn't fit in an int (the type used for integer args).
+  // So instead, we use a hack to store it as a pointer, because
+  // intptr_t should be the same size as void*.
+  static_assert(sizeof(intptr_t) <= sizeof(void*),
+                "can't fit intptr_t inside of void*");
+  return grpc_channel_arg_pointer_create(
+      const_cast<char*>(GRPC_ARG_CHANNELZ_PARENT_UUID),
+      reinterpret_cast<void*>(parent_uuid), &parent_uuid_vtable);
+}
+
+intptr_t GetParentUuidFromArgs(const grpc_channel_args& args) {
+  const grpc_arg* arg =
+      grpc_channel_args_find(&args, GRPC_ARG_CHANNELZ_PARENT_UUID);
+  if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return 0;
+  return reinterpret_cast<intptr_t>(arg->value.pointer.p);
+}
+
+//
+// BaseNode
+//
+
+BaseNode::BaseNode(EntityType type, UniquePtr<char> name)
+    : type_(type), uuid_(-1), name_(std::move(name)) {
+  // The registry will set uuid_ under its lock.
+  ChannelzRegistry::Register(this);
+}
+
+BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); }
+
+char* BaseNode::RenderJsonString() {
+  grpc_json* json = RenderJson();
+  GPR_ASSERT(json != nullptr);
+  char* json_str = grpc_json_dump_to_string(json, 0);
+  grpc_json_destroy(json);
+  return json_str;
+}
+
+//
+// CallCountingHelper
+//
+
+CallCountingHelper::CallCountingHelper() {
+  num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
+  per_cpu_counter_data_storage_.reserve(num_cores_);
+  for (size_t i = 0; i < num_cores_; ++i) {
+    per_cpu_counter_data_storage_.emplace_back();
+  }
+}
+
+void CallCountingHelper::RecordCallStarted() {
+  AtomicCounterData& data =
+      per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()];
+  data.calls_started.FetchAdd(1, MemoryOrder::RELAXED);
+  data.last_call_started_cycle.Store(gpr_get_cycle_counter(),
+                                     MemoryOrder::RELAXED);
+}
+
+void CallCountingHelper::RecordCallFailed() {
+  per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
+      .calls_failed.FetchAdd(1, MemoryOrder::RELAXED);
+}
+
+void CallCountingHelper::RecordCallSucceeded() {
+  per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
+      .calls_succeeded.FetchAdd(1, MemoryOrder::RELAXED);
+}
+
+void CallCountingHelper::CollectData(CounterData* out) {
+  for (size_t core = 0; core < num_cores_; ++core) {
+    AtomicCounterData& data = per_cpu_counter_data_storage_[core];
+
+    out->calls_started += data.calls_started.Load(MemoryOrder::RELAXED);
+    out->calls_succeeded +=
+        per_cpu_counter_data_storage_[core].calls_succeeded.Load(
+            MemoryOrder::RELAXED);
+    out->calls_failed += per_cpu_counter_data_storage_[core].calls_failed.Load(
+        MemoryOrder::RELAXED);
+    const gpr_cycle_counter last_call =
+        per_cpu_counter_data_storage_[core].last_call_started_cycle.Load(
+            MemoryOrder::RELAXED);
+    if (last_call > out->last_call_started_cycle) {
+      out->last_call_started_cycle = last_call;
+    }
+  }
+}
+
+void CallCountingHelper::PopulateCallCounts(grpc_json* json) {
+  grpc_json* json_iterator = nullptr;
+  CounterData data;
+  CollectData(&data);
+  if (data.calls_started != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "callsStarted", data.calls_started);
+  }
+  if (data.calls_succeeded != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "callsSucceeded", data.calls_succeeded);
+  }
+  if (data.calls_failed) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "callsFailed", data.calls_failed);
+  }
+  if (data.calls_started != 0) {
+    gpr_timespec ts = gpr_convert_clock_type(
+        gpr_cycle_counter_to_time(data.last_call_started_cycle),
+        GPR_CLOCK_REALTIME);
+    json_iterator =
+        grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
+                               gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+  }
+}
+
+//
+// ChannelNode
+//
+
+ChannelNode::ChannelNode(UniquePtr<char> target,
+                         size_t channel_tracer_max_nodes, intptr_t parent_uuid)
+    : BaseNode(parent_uuid == 0 ? EntityType::kTopLevelChannel
+                                : EntityType::kInternalChannel,
+               UniquePtr<char>(gpr_strdup(target.get()))),
+      target_(std::move(target)),
+      trace_(channel_tracer_max_nodes),
+      parent_uuid_(parent_uuid) {}
+
+const char* ChannelNode::GetChannelConnectivityStateChangeString(
+    grpc_connectivity_state state) {
+  switch (state) {
+    case GRPC_CHANNEL_IDLE:
+      return "Channel state change to IDLE";
+    case GRPC_CHANNEL_CONNECTING:
+      return "Channel state change to CONNECTING";
+    case GRPC_CHANNEL_READY:
+      return "Channel state change to READY";
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+      return "Channel state change to TRANSIENT_FAILURE";
+    case GRPC_CHANNEL_SHUTDOWN:
+      return "Channel state change to SHUTDOWN";
+  }
+  GPR_UNREACHABLE_CODE(return "UNKNOWN");
+}
+
+grpc_json* ChannelNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "channelId", uuid());
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  // connectivity state
+  // If low-order bit is on, then the field is set.
+  int state_field = connectivity_state_.Load(MemoryOrder::RELAXED);
+  if ((state_field & 1) != 0) {
+    grpc_connectivity_state state =
+        static_cast<grpc_connectivity_state>(state_field >> 1);
+    json = grpc_json_create_child(nullptr, json, "state", nullptr,
+                                  GRPC_JSON_OBJECT, false);
+    grpc_json_create_child(nullptr, json, "state",
+                           grpc_connectivity_state_name(state),
+                           GRPC_JSON_STRING, false);
+    json = data;
+  }
+  // populate the target.
+  GPR_ASSERT(target_.get() != nullptr);
+  grpc_json_create_child(nullptr, json, "target", target_.get(),
+                         GRPC_JSON_STRING, false);
+  // fill in the channel trace if applicable
+  grpc_json* trace_json = trace_.RenderJson();
+  if (trace_json != nullptr) {
+    trace_json->key = "trace";  // this object is named trace in channelz.proto
+    grpc_json_link_child(json, trace_json, nullptr);
+  }
+  // ask CallCountingHelper to populate trace and call count data.
+  call_counter_.PopulateCallCounts(json);
+  json = top_level_json;
+  // template method. Child classes may override this to add their specific
+  // functionality.
+  PopulateChildRefs(json);
+  return top_level_json;
+}
+
+void ChannelNode::PopulateChildRefs(grpc_json* json) {
+  MutexLock lock(&child_mu_);
+  grpc_json* json_iterator = nullptr;
+  if (!child_subchannels_.empty()) {
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false);
+    for (const auto& p : child_subchannels_) {
+      json_iterator =
+          grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
+                                 GRPC_JSON_OBJECT, false);
+      grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId",
+                                        p.first);
+    }
+  }
+  if (!child_channels_.empty()) {
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false);
+    json_iterator = nullptr;
+    for (const auto& p : child_channels_) {
+      json_iterator =
+          grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
+                                 GRPC_JSON_OBJECT, false);
+      grpc_json_add_number_string_child(json_iterator, nullptr, "channelId",
+                                        p.first);
+    }
+  }
+}
+
+void ChannelNode::SetConnectivityState(grpc_connectivity_state state) {
+  // Store with low-order bit set to indicate that the field is set.
+  int state_field = (state << 1) + 1;
+  connectivity_state_.Store(state_field, MemoryOrder::RELAXED);
+}
+
+void ChannelNode::AddChildChannel(intptr_t child_uuid) {
+  MutexLock lock(&child_mu_);
+  child_channels_.insert(MakePair(child_uuid, true));
+}
+
+void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
+  MutexLock lock(&child_mu_);
+  child_channels_.erase(child_uuid);
+}
+
+void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
+  MutexLock lock(&child_mu_);
+  child_subchannels_.insert(MakePair(child_uuid, true));
+}
+
+void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
+  MutexLock lock(&child_mu_);
+  child_subchannels_.erase(child_uuid);
+}
+
+//
+// ServerNode
+//
+
+ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes)
+    : BaseNode(EntityType::kServer, /* name */ nullptr),
+      trace_(channel_tracer_max_nodes) {}
+
+ServerNode::~ServerNode() {}
+
+void ServerNode::AddChildSocket(RefCountedPtr<SocketNode> node) {
+  MutexLock lock(&child_mu_);
+  child_sockets_.insert(MakePair(node->uuid(), std::move(node)));
+}
+
+void ServerNode::RemoveChildSocket(intptr_t child_uuid) {
+  MutexLock lock(&child_mu_);
+  child_sockets_.erase(child_uuid);
+}
+
+void ServerNode::AddChildListenSocket(RefCountedPtr<ListenSocketNode> node) {
+  MutexLock lock(&child_mu_);
+  child_listen_sockets_.insert(MakePair(node->uuid(), std::move(node)));
+}
+
+void ServerNode::RemoveChildListenSocket(intptr_t child_uuid) {
+  MutexLock lock(&child_mu_);
+  child_listen_sockets_.erase(child_uuid);
+}
+
+char* ServerNode::RenderServerSockets(intptr_t start_socket_id,
+                                      intptr_t max_results) {
+  // If user does not set max_results, we choose 500.
+  size_t pagination_limit = max_results == 0 ? 500 : max_results;
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  MutexLock lock(&child_mu_);
+  size_t sockets_rendered = 0;
+  if (!child_sockets_.empty()) {
+    // Create list of socket refs
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
+    const size_t limit = GPR_MIN(child_sockets_.size(), pagination_limit);
+    for (auto it = child_sockets_.lower_bound(start_socket_id);
+         it != child_sockets_.end() && sockets_rendered < limit;
+         ++it, ++sockets_rendered) {
+      grpc_json* socket_ref_json = grpc_json_create_child(
+          nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false);
+      json_iterator = grpc_json_add_number_string_child(
+          socket_ref_json, nullptr, "socketId", it->first);
+      grpc_json_create_child(json_iterator, socket_ref_json, "name",
+                             it->second->name(), GRPC_JSON_STRING, false);
+    }
+  }
+  if (sockets_rendered == child_sockets_.size()) {
+    json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr,
+                                           GRPC_JSON_TRUE, false);
+  }
+  char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+  grpc_json_destroy(top_level_json);
+  return json_str;
+}
+
+grpc_json* ServerNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "serverId", uuid());
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  // fill in the channel trace if applicable
+  grpc_json* trace_json = trace_.RenderJson();
+  if (trace_json != nullptr) {
+    trace_json->key = "trace";  // this object is named trace in channelz.proto
+    grpc_json_link_child(json, trace_json, nullptr);
+  }
+  // ask CallCountingHelper to populate trace and call count data.
+  call_counter_.PopulateCallCounts(json);
+  json = top_level_json;
+  // Render listen sockets
+  MutexLock lock(&child_mu_);
+  if (!child_listen_sockets_.empty()) {
+    grpc_json* array_parent = grpc_json_create_child(
+        nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false);
+    for (const auto& it : child_listen_sockets_) {
+      json_iterator =
+          grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr,
+                                 GRPC_JSON_OBJECT, false);
+      grpc_json* sibling_iterator = grpc_json_add_number_string_child(
+          json_iterator, nullptr, "socketId", it.first);
+      grpc_json_create_child(sibling_iterator, json_iterator, "name",
+                             it.second->name(), GRPC_JSON_STRING, false);
+    }
+  }
+  return top_level_json;
+}
+
+//
+// SocketNode
+//
+
+namespace {
+
+void PopulateSocketAddressJson(grpc_json* json, const char* name,
+                               const char* addr_str) {
+  if (addr_str == nullptr) return;
+  grpc_json* json_iterator = nullptr;
+  json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  grpc_uri* uri = grpc_uri_parse(addr_str, true);
+  if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) ||
+                           (strcmp(uri->scheme, "ipv6") == 0))) {
+    const char* host_port = uri->path;
+    if (*host_port == '/') ++host_port;
+    UniquePtr<char> host;
+    UniquePtr<char> port;
+    GPR_ASSERT(SplitHostPort(host_port, &host, &port));
+    int port_num = -1;
+    if (port != nullptr) {
+      port_num = atoi(port.get());
+    }
+    char* b64_host =
+        grpc_base64_encode(host.get(), strlen(host.get()), false, false);
+    json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address",
+                                           nullptr, GRPC_JSON_OBJECT, false);
+    json = json_iterator;
+    json_iterator = nullptr;
+    json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                      "port", port_num);
+    json_iterator = grpc_json_create_child(json_iterator, json, "ip_address",
+                                           b64_host, GRPC_JSON_STRING, true);
+  } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) {
+    json_iterator = grpc_json_create_child(json_iterator, json, "uds_address",
+                                           nullptr, GRPC_JSON_OBJECT, false);
+    json = json_iterator;
+    json_iterator = nullptr;
+    json_iterator =
+        grpc_json_create_child(json_iterator, json, "filename",
+                               gpr_strdup(uri->path), GRPC_JSON_STRING, true);
+  } else {
+    json_iterator = grpc_json_create_child(json_iterator, json, "other_address",
+                                           nullptr, GRPC_JSON_OBJECT, false);
+    json = json_iterator;
+    json_iterator = nullptr;
+    json_iterator = grpc_json_create_child(json_iterator, json, "name",
+                                           addr_str, GRPC_JSON_STRING, false);
+  }
+  grpc_uri_destroy(uri);
+}
+
+}  // namespace
+
+SocketNode::SocketNode(UniquePtr<char> local, UniquePtr<char> remote,
+                       UniquePtr<char> name)
+    : BaseNode(EntityType::kSocket, std::move(name)),
+      local_(std::move(local)),
+      remote_(std::move(remote)) {}
+
+void SocketNode::RecordStreamStartedFromLocal() {
+  gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
+  gpr_atm_no_barrier_store(&last_local_stream_created_cycle_,
+                           gpr_get_cycle_counter());
+}
+
+void SocketNode::RecordStreamStartedFromRemote() {
+  gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast<gpr_atm>(1));
+  gpr_atm_no_barrier_store(&last_remote_stream_created_cycle_,
+                           gpr_get_cycle_counter());
+}
+
+void SocketNode::RecordMessagesSent(uint32_t num_sent) {
+  gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast<gpr_atm>(num_sent));
+  gpr_atm_no_barrier_store(&last_message_sent_cycle_, gpr_get_cycle_counter());
+}
+
+void SocketNode::RecordMessageReceived() {
+  gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast<gpr_atm>(1));
+  gpr_atm_no_barrier_store(&last_message_received_cycle_,
+                           gpr_get_cycle_counter());
+}
+
+grpc_json* SocketNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "socketId", uuid());
+  json_iterator = grpc_json_create_child(json_iterator, json, "name", name(),
+                                         GRPC_JSON_STRING, false);
+  json = top_level_json;
+  PopulateSocketAddressJson(json, "remote", remote_.get());
+  PopulateSocketAddressJson(json, "local", local_.get());
+  // reset json iterators to top level object
+  json = top_level_json;
+  json_iterator = nullptr;
+  // create and fill the data child.
+  grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr,
+                                           GRPC_JSON_OBJECT, false);
+  json = data;
+  json_iterator = nullptr;
+  gpr_timespec ts;
+  gpr_atm streams_started = gpr_atm_no_barrier_load(&streams_started_);
+  if (streams_started != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "streamsStarted", streams_started);
+    gpr_cycle_counter last_local_stream_created_cycle =
+        gpr_atm_no_barrier_load(&last_local_stream_created_cycle_);
+    if (last_local_stream_created_cycle != 0) {
+      ts = gpr_convert_clock_type(
+          gpr_cycle_counter_to_time(last_local_stream_created_cycle),
+          GPR_CLOCK_REALTIME);
+      json_iterator = grpc_json_create_child(
+          json_iterator, json, "lastLocalStreamCreatedTimestamp",
+          gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    }
+    gpr_cycle_counter last_remote_stream_created_cycle =
+        gpr_atm_no_barrier_load(&last_remote_stream_created_cycle_);
+    if (last_remote_stream_created_cycle != 0) {
+      ts = gpr_convert_clock_type(
+          gpr_cycle_counter_to_time(last_remote_stream_created_cycle),
+          GPR_CLOCK_REALTIME);
+      json_iterator = grpc_json_create_child(
+          json_iterator, json, "lastRemoteStreamCreatedTimestamp",
+          gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    }
+  }
+  gpr_atm streams_succeeded = gpr_atm_no_barrier_load(&streams_succeeded_);
+  if (streams_succeeded != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "streamsSucceeded", streams_succeeded);
+  }
+  gpr_atm streams_failed = gpr_atm_no_barrier_load(&streams_failed_);
+  if (streams_failed) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "streamsFailed", streams_failed);
+  }
+  gpr_atm messages_sent = gpr_atm_no_barrier_load(&messages_sent_);
+  if (messages_sent != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "messagesSent", messages_sent);
+    ts = gpr_convert_clock_type(
+        gpr_cycle_counter_to_time(
+            gpr_atm_no_barrier_load(&last_message_sent_cycle_)),
+        GPR_CLOCK_REALTIME);
+    json_iterator =
+        grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp",
+                               gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+  }
+  gpr_atm messages_received = gpr_atm_no_barrier_load(&messages_received_);
+  if (messages_received != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "messagesReceived", messages_received);
+    ts = gpr_convert_clock_type(
+        gpr_cycle_counter_to_time(
+            gpr_atm_no_barrier_load(&last_message_received_cycle_)),
+        GPR_CLOCK_REALTIME);
+    json_iterator = grpc_json_create_child(
+        json_iterator, json, "lastMessageReceivedTimestamp",
+        gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+  }
+  gpr_atm keepalives_sent = gpr_atm_no_barrier_load(&keepalives_sent_);
+  if (keepalives_sent != 0) {
+    json_iterator = grpc_json_add_number_string_child(
+        json, json_iterator, "keepAlivesSent", keepalives_sent);
+  }
+  return top_level_json;
+}
+
+//
+// ListenSocketNode
+//
+
+ListenSocketNode::ListenSocketNode(UniquePtr<char> local_addr,
+                                   UniquePtr<char> name)
+    : BaseNode(EntityType::kSocket, std::move(name)),
+      local_addr_(std::move(local_addr)) {}
+
+grpc_json* ListenSocketNode::RenderJson() {
+  // We need to track these three json objects to build our object
+  grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+  grpc_json* json = top_level_json;
+  grpc_json* json_iterator = nullptr;
+  // create and fill the ref child
+  json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr,
+                                         GRPC_JSON_OBJECT, false);
+  json = json_iterator;
+  json_iterator = nullptr;
+  json_iterator = grpc_json_add_number_string_child(json, json_iterator,
+                                                    "socketId", uuid());
+  json_iterator = grpc_json_create_child(json_iterator, json, "name", name(),
+                                         GRPC_JSON_STRING, false);
+  json = top_level_json;
+  PopulateSocketAddressJson(json, "local", local_addr_.get());
+
+  return top_level_json;
+}
+
+}  // namespace channelz
+}  // namespace grpc_core