Imported Upstream version 1.41.0
[platform/upstream/grpc.git] / src / core / lib / channel / channelz.cc
index 4b13037..6aaf557 100644 (file)
 
 #include "src/core/lib/channel/channelz.h"
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atomic>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/strip.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/address_utils/sockaddr_utils.h"
 #include "src/core/lib/channel/channelz_registry.h"
 #include "src/core/lib/channel/status_util.h"
-#include "src/core/lib/gpr/host_port.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/iomgr/resolve_address.h"
 #include "src/core/lib/slice/b64.h"
 #include "src/core/lib/slice/slice_internal.h"
 #include "src/core/lib/surface/channel.h"
@@ -48,56 +56,20 @@ 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) : type_(type), uuid_(-1) {
+BaseNode::BaseNode(EntityType type, std::string 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;
+std::string BaseNode::RenderJsonString() {
+  Json json = RenderJson();
+  return json.Dump();
 }
 
 //
@@ -106,77 +78,64 @@ char* BaseNode::RenderJsonString() {
 
 CallCountingHelper::CallCountingHelper() {
   num_cores_ = GPR_MAX(1, gpr_cpu_num_cores());
-  per_cpu_counter_data_storage_ = static_cast<AtomicCounterData*>(
-      gpr_zalloc(sizeof(AtomicCounterData) * num_cores_));
-}
-
-CallCountingHelper::~CallCountingHelper() {
-  gpr_free(per_cpu_counter_data_storage_);
+  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() {
-  gpr_atm_no_barrier_fetch_add(
-      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
-           .calls_started,
-      static_cast<gpr_atm>(1));
-  gpr_atm_no_barrier_store(
-      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
-           .last_call_started_millis,
-      (gpr_atm)ExecCtx::Get()->Now());
+  AtomicCounterData& data =
+      per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()];
+  data.calls_started.fetch_add(1, std::memory_order_relaxed);
+  data.last_call_started_cycle.store(gpr_get_cycle_counter(),
+                                     std::memory_order_relaxed);
 }
 
 void CallCountingHelper::RecordCallFailed() {
-  gpr_atm_no_barrier_fetch_add(
-      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
-           .calls_failed,
-      static_cast<gpr_atm>(1));
+  per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
+      .calls_failed.fetch_add(1, std::memory_order_relaxed);
 }
 
 void CallCountingHelper::RecordCallSucceeded() {
-  gpr_atm_no_barrier_fetch_add(
-      &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()]
-           .calls_succeeded,
-      static_cast<gpr_atm>(1));
+  per_cpu_counter_data_storage_[ExecCtx::Get()->starting_cpu()]
+      .calls_succeeded.fetch_add(1, std::memory_order_relaxed);
 }
 
 void CallCountingHelper::CollectData(CounterData* out) {
   for (size_t core = 0; core < num_cores_; ++core) {
-    out->calls_started += gpr_atm_no_barrier_load(
-        &per_cpu_counter_data_storage_[core].calls_started);
-    out->calls_succeeded += gpr_atm_no_barrier_load(
-        &per_cpu_counter_data_storage_[core].calls_succeeded);
-    out->calls_failed += gpr_atm_no_barrier_load(
-        &per_cpu_counter_data_storage_[core].calls_failed);
-    gpr_atm last_call = gpr_atm_no_barrier_load(
-        &per_cpu_counter_data_storage_[core].last_call_started_millis);
-    if (last_call > out->last_call_started_millis) {
-      out->last_call_started_millis = last_call;
+    AtomicCounterData& data = per_cpu_counter_data_storage_[core];
+
+    out->calls_started += data.calls_started.load(std::memory_order_relaxed);
+    out->calls_succeeded +=
+        per_cpu_counter_data_storage_[core].calls_succeeded.load(
+            std::memory_order_relaxed);
+    out->calls_failed += per_cpu_counter_data_storage_[core].calls_failed.load(
+        std::memory_order_relaxed);
+    const gpr_cycle_counter last_call =
+        per_cpu_counter_data_storage_[core].last_call_started_cycle.load(
+            std::memory_order_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;
+void CallCountingHelper::PopulateCallCounts(Json::Object* json) {
   CounterData data;
   CollectData(&data);
   if (data.calls_started != 0) {
-    json_iterator = grpc_json_add_number_string_child(
-        json, json_iterator, "callsStarted", data.calls_started);
+    (*json)["callsStarted"] = std::to_string(data.calls_started);
+    gpr_timespec ts = gpr_convert_clock_type(
+        gpr_cycle_counter_to_time(data.last_call_started_cycle),
+        GPR_CLOCK_REALTIME);
+    (*json)["lastCallStartedTimestamp"] = gpr_format_timespec(ts);
   }
   if (data.calls_succeeded != 0) {
-    json_iterator = grpc_json_add_number_string_child(
-        json, json_iterator, "callsSucceeded", data.calls_succeeded);
+    (*json)["callsSucceeded"] = std::to_string(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 = grpc_millis_to_timespec(data.last_call_started_millis,
-                                              GPR_CLOCK_REALTIME);
-    json_iterator =
-        grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
-                               gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    (*json)["callsFailed"] = std::to_string(data.calls_failed);
   }
 }
 
@@ -184,103 +143,97 @@ void CallCountingHelper::PopulateCallCounts(grpc_json* json) {
 // ChannelNode
 //
 
-ChannelNode::ChannelNode(UniquePtr<char> target,
-                         size_t channel_tracer_max_nodes, intptr_t parent_uuid)
-    : BaseNode(parent_uuid == 0 ? EntityType::kTopLevelChannel
-                                : EntityType::kInternalChannel),
+ChannelNode::ChannelNode(std::string target, size_t channel_tracer_max_nodes,
+                         bool is_internal_channel)
+    : BaseNode(is_internal_channel ? EntityType::kInternalChannel
+                                   : EntityType::kTopLevelChannel,
+               target),
       target_(std::move(target)),
-      trace_(channel_tracer_max_nodes),
-      parent_uuid_(parent_uuid) {}
-
-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
+      trace_(channel_tracer_max_nodes) {}
+
+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");
+}
+
+Json ChannelNode::RenderJson() {
+  Json::Object data = {
+      {"target", target_},
+  };
+  // Connectivity state.
   // If low-order bit is on, then the field is set.
-  int state_field = connectivity_state_.Load(MemoryOrder::RELAXED);
+  int state_field = connectivity_state_.load(std::memory_order_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;
+    data["state"] = Json::Object{
+        {"state", ConnectivityStateName(state)},
+    };
   }
-  // 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);
+  // Fill in the channel trace if applicable.
+  Json trace_json = trace_.RenderJson();
+  if (trace_json.type() != Json::Type::JSON_NULL) {
+    data["trace"] = std::move(trace_json);
   }
-  // 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
+  // Ask CallCountingHelper to populate call count data.
+  call_counter_.PopulateCallCounts(&data);
+  // Construct outer object.
+  Json::Object json = {
+      {"ref",
+       Json::Object{
+           {"channelId", std::to_string(uuid())},
+       }},
+      {"data", std::move(data)},
+  };
+  // Template method. Child classes may override this to add their specific
   // functionality.
-  PopulateChildRefs(json);
-  return top_level_json;
+  PopulateChildRefs(&json);
+  return json;
 }
 
-void ChannelNode::PopulateChildRefs(grpc_json* json) {
+void ChannelNode::PopulateChildRefs(Json::Object* 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);
+    Json::Array array;
+    for (intptr_t subchannel_uuid : child_subchannels_) {
+      array.emplace_back(Json::Object{
+          {"subchannelId", std::to_string(subchannel_uuid)},
+      });
     }
+    (*json)["subchannelRef"] = std::move(array);
   }
   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);
+    Json::Array array;
+    for (intptr_t channel_uuid : child_channels_) {
+      array.emplace_back(Json::Object{
+          {"channelId", std::to_string(channel_uuid)},
+      });
     }
+    (*json)["channelRef"] = std::move(array);
   }
 }
 
 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);
+  connectivity_state_.store(state_field, std::memory_order_relaxed);
 }
 
 void ChannelNode::AddChildChannel(intptr_t child_uuid) {
   MutexLock lock(&child_mu_);
-  child_channels_.insert(MakePair(child_uuid, true));
+  child_channels_.insert(child_uuid);
 }
 
 void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
@@ -290,7 +243,7 @@ void ChannelNode::RemoveChildChannel(intptr_t child_uuid) {
 
 void ChannelNode::AddChildSubchannel(intptr_t child_uuid) {
   MutexLock lock(&child_mu_);
-  child_subchannels_.insert(MakePair(child_uuid, true));
+  child_subchannels_.insert(child_uuid);
 }
 
 void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
@@ -302,89 +255,167 @@ void ChannelNode::RemoveChildSubchannel(intptr_t child_uuid) {
 // ServerNode
 //
 
-ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes)
-    : BaseNode(EntityType::kServer),
-      server_(server),
-      trace_(channel_tracer_max_nodes) {}
+ServerNode::ServerNode(size_t channel_tracer_max_nodes)
+    : BaseNode(EntityType::kServer, ""), trace_(channel_tracer_max_nodes) {}
 
 ServerNode::~ServerNode() {}
 
-char* ServerNode::RenderServerSockets(intptr_t start_socket_id,
-                                      intptr_t max_results) {
-  // if user does not set max_results, we choose 500.
+void ServerNode::AddChildSocket(RefCountedPtr<SocketNode> node) {
+  MutexLock lock(&child_mu_);
+  child_sockets_.insert(std::make_pair(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(std::make_pair(node->uuid(), std::move(node)));
+}
+
+void ServerNode::RemoveChildListenSocket(intptr_t child_uuid) {
+  MutexLock lock(&child_mu_);
+  child_listen_sockets_.erase(child_uuid);
+}
+
+std::string ServerNode::RenderServerSockets(intptr_t start_socket_id,
+                                            intptr_t max_results) {
+  GPR_ASSERT(start_socket_id >= 0);
+  GPR_ASSERT(max_results >= 0);
+  // 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;
-  ChildSocketsList socket_refs;
-  grpc_server_populate_server_sockets(server_, &socket_refs, start_socket_id);
-  // declared early so it can be used outside of the loop.
-  size_t i = 0;
-  if (!socket_refs.empty()) {
-    // create list of socket refs
-    grpc_json* array_parent = grpc_json_create_child(
-        nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false);
-    for (i = 0; i < GPR_MIN(socket_refs.size(), pagination_limit); ++i) {
-      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", socket_refs[i]->uuid());
-      grpc_json_create_child(json_iterator, socket_ref_json, "name",
-                             socket_refs[i]->remote(), GRPC_JSON_STRING, false);
+  Json::Object object;
+  {
+    MutexLock lock(&child_mu_);
+    size_t sockets_rendered = 0;
+    // Create list of socket refs.
+    Json::Array array;
+    auto it = child_sockets_.lower_bound(start_socket_id);
+    for (; it != child_sockets_.end() && sockets_rendered < pagination_limit;
+         ++it, ++sockets_rendered) {
+      array.emplace_back(Json::Object{
+          {"socketId", std::to_string(it->first)},
+          {"name", it->second->name()},
+      });
     }
+    object["socketRef"] = std::move(array);
+    if (it == child_sockets_.end()) object["end"] = true;
   }
-  if (i == socket_refs.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);
+  Json json = std::move(object);
+  return json.Dump();
+}
+
+Json ServerNode::RenderJson() {
+  Json::Object data;
+  // Fill in the channel trace if applicable.
+  Json trace_json = trace_.RenderJson();
+  if (trace_json.type() != Json::Type::JSON_NULL) {
+    data["trace"] = std::move(trace_json);
   }
-  // ask CallCountingHelper to populate trace and call count data.
-  call_counter_.PopulateCallCounts(json);
-  json = top_level_json;
-  ChildRefsList listen_sockets;
-  grpc_server_populate_listen_sockets(server_, &listen_sockets);
-  if (!listen_sockets.empty()) {
-    grpc_json* array_parent = grpc_json_create_child(
-        nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false);
-    for (size_t i = 0; i < listen_sockets.size(); ++i) {
-      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, "socketId",
-                                        listen_sockets[i]);
+  // Ask CallCountingHelper to populate call count data.
+  call_counter_.PopulateCallCounts(&data);
+  // Construct top-level object.
+  Json::Object object = {
+      {"ref",
+       Json::Object{
+           {"serverId", std::to_string(uuid())},
+       }},
+      {"data", std::move(data)},
+  };
+  // Render listen sockets.
+  {
+    MutexLock lock(&child_mu_);
+    if (!child_listen_sockets_.empty()) {
+      Json::Array array;
+      for (const auto& it : child_listen_sockets_) {
+        array.emplace_back(Json::Object{
+            {"socketId", std::to_string(it.first)},
+            {"name", it.second->name()},
+        });
+      }
+      object["listenSocket"] = std::move(array);
     }
   }
-  return top_level_json;
+  return object;
+}
+
+//
+// SocketNode::Security::Tls
+//
+
+Json SocketNode::Security::Tls::RenderJson() {
+  Json::Object data;
+  if (type == NameType::kStandardName) {
+    data["standard_name"] = name;
+  } else if (type == NameType::kOtherName) {
+    data["other_name"] = name;
+  }
+  if (!local_certificate.empty()) {
+    data["local_certificate"] = absl::Base64Escape(local_certificate);
+  }
+  if (!remote_certificate.empty()) {
+    data["remote_certificate"] = absl::Base64Escape(remote_certificate);
+  }
+  return data;
+}
+
+//
+// SocketNode::Security
+//
+
+Json SocketNode::Security::RenderJson() {
+  Json::Object data;
+  switch (type) {
+    case ModelType::kUnset:
+      break;
+    case ModelType::kTls:
+      if (tls) {
+        data["tls"] = tls->RenderJson();
+      }
+      break;
+    case ModelType::kOther:
+      if (other) {
+        data["other"] = *other;
+      }
+      break;
+  }
+  return data;
+}
+
+namespace {
+
+void* SecurityArgCopy(void* p) {
+  SocketNode::Security* xds_certificate_provider =
+      static_cast<SocketNode::Security*>(p);
+  return xds_certificate_provider->Ref().release();
+}
+
+void SecurityArgDestroy(void* p) {
+  SocketNode::Security* xds_certificate_provider =
+      static_cast<SocketNode::Security*>(p);
+  xds_certificate_provider->Unref();
+}
+
+int SecurityArgCmp(void* p, void* q) { return GPR_ICMP(p, q); }
+
+const grpc_arg_pointer_vtable kChannelArgVtable = {
+    SecurityArgCopy, SecurityArgDestroy, SecurityArgCmp};
+
+}  // namespace
+
+grpc_arg SocketNode::Security::MakeChannelArg() const {
+  return grpc_channel_arg_pointer_create(
+      const_cast<char*>(GRPC_ARG_CHANNELZ_SECURITY),
+      const_cast<SocketNode::Security*>(this), &kChannelArgVtable);
+}
+
+RefCountedPtr<SocketNode::Security> SocketNode::Security::GetFromChannelArgs(
+    const grpc_channel_args* args) {
+  Security* security = grpc_channel_args_find_pointer<Security>(
+      args, GRPC_ARG_CHANNELZ_SECURITY);
+  return security != nullptr ? security->Ref() : nullptr;
 }
 
 //
@@ -393,197 +424,172 @@ grpc_json* ServerNode::RenderJson() {
 
 namespace {
 
-void PopulateSocketAddressJson(grpc_json* json, const char* name,
+void PopulateSocketAddressJson(Json::Object* 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;
-    char* host = nullptr;
-    char* port = nullptr;
-    GPR_ASSERT(gpr_split_host_port(host_port, &host, &port));
+  Json::Object data;
+  absl::StatusOr<URI> uri = URI::Parse(addr_str);
+  if (uri.ok() && (uri->scheme() == "ipv4" || uri->scheme() == "ipv6")) {
+    std::string host;
+    std::string port;
+    GPR_ASSERT(
+        SplitHostPort(absl::StripPrefix(uri->path(), "/"), &host, &port));
     int port_num = -1;
-    if (port != nullptr) {
-      port_num = atoi(port);
+    if (!port.empty()) {
+      port_num = atoi(port.data());
     }
-    char* b64_host = grpc_base64_encode(host, strlen(host), 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);
-    gpr_free(host);
-    gpr_free(port);
-  } 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);
+    grpc_resolved_address resolved_host;
+    grpc_error_handle error =
+        grpc_string_to_sockaddr(&resolved_host, host.c_str(), port_num);
+    if (error == GRPC_ERROR_NONE) {
+      std::string packed_host = grpc_sockaddr_get_packed_host(&resolved_host);
+      std::string b64_host = absl::Base64Escape(packed_host);
+      data["tcpip_address"] = Json::Object{
+          {"port", port_num},
+          {"ip_address", b64_host},
+      };
+      (*json)[name] = std::move(data);
+      return;
+    }
+    GRPC_ERROR_UNREF(error);
+  }
+  if (uri.ok() && uri->scheme() == "unix") {
+    data["uds_address"] = Json::Object{
+        {"filename", uri->path()},
+    };
   } 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);
+    data["other_address"] = Json::Object{
+        {"name", addr_str},
+    };
   }
-  grpc_uri_destroy(uri);
+  (*json)[name] = std::move(data);
 }
 
 }  // namespace
 
-SocketNode::SocketNode(UniquePtr<char> local, UniquePtr<char> remote)
-    : BaseNode(EntityType::kSocket),
+SocketNode::SocketNode(std::string local, std::string remote, std::string name,
+                       RefCountedPtr<Security> security)
+    : BaseNode(EntityType::kSocket, std::move(name)),
       local_(std::move(local)),
-      remote_(std::move(remote)) {}
+      remote_(std::move(remote)),
+      security_(std::move(security)) {}
 
 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_millis_,
-                           (gpr_atm)ExecCtx::Get()->Now());
+  streams_started_.fetch_add(1, std::memory_order_relaxed);
+  last_local_stream_created_cycle_.store(gpr_get_cycle_counter(),
+                                         std::memory_order_relaxed);
 }
 
 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_millis_,
-                           (gpr_atm)ExecCtx::Get()->Now());
+  streams_started_.fetch_add(1, std::memory_order_relaxed);
+  last_remote_stream_created_cycle_.store(gpr_get_cycle_counter(),
+                                          std::memory_order_relaxed);
 }
 
 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_millis_,
-                           (gpr_atm)ExecCtx::Get()->Now());
+  messages_sent_.fetch_add(num_sent, std::memory_order_relaxed);
+  last_message_sent_cycle_.store(gpr_get_cycle_counter(),
+                                 std::memory_order_relaxed);
 }
 
 void SocketNode::RecordMessageReceived() {
-  gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast<gpr_atm>(1));
-  gpr_atm_no_barrier_store(&last_message_received_millis_,
-                           (gpr_atm)ExecCtx::Get()->Now());
-}
-
-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 = 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;
+  messages_received_.fetch_add(1, std::memory_order_relaxed);
+  last_message_received_cycle_.store(gpr_get_cycle_counter(),
+                                     std::memory_order_relaxed);
+}
+
+Json SocketNode::RenderJson() {
+  // Create and fill the data child.
+  Json::Object data;
   gpr_timespec ts;
-  gpr_atm streams_started = gpr_atm_no_barrier_load(&streams_started_);
+  int64_t streams_started = streams_started_.load(std::memory_order_relaxed);
   if (streams_started != 0) {
-    json_iterator = grpc_json_add_number_string_child(
-        json, json_iterator, "streamsStarted", streams_started);
-    gpr_atm last_local_stream_created_millis =
-        gpr_atm_no_barrier_load(&last_local_stream_created_millis_);
-    if (last_local_stream_created_millis != 0) {
-      ts = grpc_millis_to_timespec(last_local_stream_created_millis,
-                                   GPR_CLOCK_REALTIME);
-      json_iterator = grpc_json_create_child(
-          json_iterator, json, "lastLocalStreamCreatedTimestamp",
-          gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    data["streamsStarted"] = std::to_string(streams_started);
+    gpr_cycle_counter last_local_stream_created_cycle =
+        last_local_stream_created_cycle_.load(std::memory_order_relaxed);
+    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);
+      data["lastLocalStreamCreatedTimestamp"] = gpr_format_timespec(ts);
     }
-    gpr_atm last_remote_stream_created_millis =
-        gpr_atm_no_barrier_load(&last_remote_stream_created_millis_);
-    if (last_remote_stream_created_millis != 0) {
-      ts = grpc_millis_to_timespec(last_remote_stream_created_millis,
-                                   GPR_CLOCK_REALTIME);
-      json_iterator = grpc_json_create_child(
-          json_iterator, json, "lastRemoteStreamCreatedTimestamp",
-          gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    gpr_cycle_counter last_remote_stream_created_cycle =
+        last_remote_stream_created_cycle_.load(std::memory_order_relaxed);
+    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);
+      data["lastRemoteStreamCreatedTimestamp"] = gpr_format_timespec(ts);
     }
   }
-  gpr_atm streams_succeeded = gpr_atm_no_barrier_load(&streams_succeeded_);
+  int64_t streams_succeeded =
+      streams_succeeded_.load(std::memory_order_relaxed);
   if (streams_succeeded != 0) {
-    json_iterator = grpc_json_add_number_string_child(
-        json, json_iterator, "streamsSucceeded", streams_succeeded);
+    data["streamsSucceeded"] = std::to_string(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);
+  int64_t streams_failed = streams_failed_.load(std::memory_order_relaxed);
+  if (streams_failed != 0) {
+    data["streamsFailed"] = std::to_string(streams_failed);
   }
-  gpr_atm messages_sent = gpr_atm_no_barrier_load(&messages_sent_);
+  int64_t messages_sent = messages_sent_.load(std::memory_order_relaxed);
   if (messages_sent != 0) {
-    json_iterator = grpc_json_add_number_string_child(
-        json, json_iterator, "messagesSent", messages_sent);
-    ts = grpc_millis_to_timespec(
-        gpr_atm_no_barrier_load(&last_message_sent_millis_),
+    data["messagesSent"] = std::to_string(messages_sent);
+    ts = gpr_convert_clock_type(
+        gpr_cycle_counter_to_time(
+            last_message_sent_cycle_.load(std::memory_order_relaxed)),
         GPR_CLOCK_REALTIME);
-    json_iterator =
-        grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp",
-                               gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    data["lastMessageSentTimestamp"] = gpr_format_timespec(ts);
   }
-  gpr_atm messages_received = gpr_atm_no_barrier_load(&messages_received_);
+  int64_t messages_received =
+      messages_received_.load(std::memory_order_relaxed);
   if (messages_received != 0) {
-    json_iterator = grpc_json_add_number_string_child(
-        json, json_iterator, "messagesReceived", messages_received);
-    ts = grpc_millis_to_timespec(
-        gpr_atm_no_barrier_load(&last_message_received_millis_),
+    data["messagesReceived"] = std::to_string(messages_received);
+    ts = gpr_convert_clock_type(
+        gpr_cycle_counter_to_time(
+            last_message_received_cycle_.load(std::memory_order_relaxed)),
         GPR_CLOCK_REALTIME);
-    json_iterator = grpc_json_create_child(
-        json_iterator, json, "lastMessageReceivedTimestamp",
-        gpr_format_timespec(ts), GRPC_JSON_STRING, true);
+    data["lastMessageReceivedTimestamp"] = gpr_format_timespec(ts);
   }
-  gpr_atm keepalives_sent = gpr_atm_no_barrier_load(&keepalives_sent_);
+  int64_t keepalives_sent = keepalives_sent_.load(std::memory_order_relaxed);
   if (keepalives_sent != 0) {
-    json_iterator = grpc_json_add_number_string_child(
-        json, json_iterator, "keepAlivesSent", keepalives_sent);
+    data["keepAlivesSent"] = std::to_string(keepalives_sent);
+  }
+  // Create and fill the parent object.
+  Json::Object object = {
+      {"ref",
+       Json::Object{
+           {"socketId", std::to_string(uuid())},
+           {"name", name()},
+       }},
+      {"data", std::move(data)},
+  };
+  if (security_ != nullptr &&
+      security_->type != SocketNode::Security::ModelType::kUnset) {
+    object["security"] = security_->RenderJson();
   }
-  return top_level_json;
+  PopulateSocketAddressJson(&object, "remote", remote_.c_str());
+  PopulateSocketAddressJson(&object, "local", local_.c_str());
+  return object;
 }
 
 //
 // ListenSocketNode
 //
 
-ListenSocketNode::ListenSocketNode(UniquePtr<char> local_addr)
-    : BaseNode(EntityType::kSocket), 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 = top_level_json;
-  PopulateSocketAddressJson(json, "local", local_addr_.get());
-
-  return top_level_json;
+ListenSocketNode::ListenSocketNode(std::string local_addr, std::string name)
+    : BaseNode(EntityType::kSocket, std::move(name)),
+      local_addr_(std::move(local_addr)) {}
+
+Json ListenSocketNode::RenderJson() {
+  Json::Object object = {
+      {"ref",
+       Json::Object{
+           {"socketId", std::to_string(uuid())},
+           {"name", name()},
+       }},
+  };
+  PopulateSocketAddressJson(&object, "local", local_addr_.c_str());
+  return object;
 }
 
 }  // namespace channelz