aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar ncteisen <ncteisen@gmail.com>2018-07-18 18:31:09 -0700
committerGravatar ncteisen <ncteisen@gmail.com>2018-07-19 08:15:49 -0700
commitbbee13661c82c13e2f1b728c94f9535112f54d92 (patch)
treeed60505ea44cdde179e837ff13050445afe7809b
parent481c1d57e7fc92f74c79b9aa0f7c8f0eaa51000b (diff)
Add channelz subchannel support
-rw-r--r--include/grpc/grpc.h4
-rw-r--r--src/core/ext/filters/client_channel/client_channel_channelz.cc29
-rw-r--r--src/core/ext/filters/client_channel/client_channel_channelz.h35
-rw-r--r--src/core/ext/filters/client_channel/subchannel.cc31
-rw-r--r--src/core/ext/filters/client_channel/subchannel.h2
-rw-r--r--src/core/lib/channel/channel_trace.cc30
-rw-r--r--src/core/lib/channel/channelz.cc142
-rw-r--r--src/core/lib/channel/channelz.h131
-rw-r--r--src/core/lib/channel/channelz_registry.cc16
-rw-r--r--src/core/lib/surface/channel.cc3
-rw-r--r--test/core/channel/channelz_test.cc29
-rw-r--r--test/cpp/util/channel_trace_proto_helper.cc4
-rw-r--r--test/cpp/util/channel_trace_proto_helper.h1
13 files changed, 354 insertions, 103 deletions
diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h
index f0eb2c0121..942c83bcde 100644
--- a/include/grpc/grpc.h
+++ b/include/grpc/grpc.h
@@ -477,6 +477,10 @@ GRPCAPI char* grpc_channelz_get_top_channels(intptr_t start_channel_id);
is allocated and must be freed by the application. */
GRPCAPI char* grpc_channelz_get_channel(intptr_t channel_id);
+/* Returns a single Subchannel, or else a NOT_FOUND code. The returned string
+ is allocated and must be freed by the application. */
+GRPCAPI char* grpc_channelz_get_subchannel(intptr_t subchannel_id);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/core/ext/filters/client_channel/client_channel_channelz.cc b/src/core/ext/filters/client_channel/client_channel_channelz.cc
index d43e9ea67a..5a0eec5d15 100644
--- a/src/core/ext/filters/client_channel/client_channel_channelz.cc
+++ b/src/core/ext/filters/client_channel/client_channel_channelz.cc
@@ -24,6 +24,8 @@
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/transport/connectivity_state.h"
+#include <grpc/support/string_util.h>
+
namespace grpc_core {
namespace channelz {
namespace {
@@ -109,5 +111,32 @@ RefCountedPtr<ChannelNode> ClientChannelNode::MakeClientChannelNode(
channel, channel_tracer_max_nodes, is_top_level_channel);
}
+ClientChannelSubchannelNode::ClientChannelSubchannelNode(
+ size_t channel_tracer_max_nodes, grpc_subchannel* subchannel)
+ : SubchannelNode(channel_tracer_max_nodes), subchannel_(subchannel) {
+ target_ =
+ UniquePtr<char>(gpr_strdup(grpc_subchannel_get_target(subchannel_)));
+}
+
+void ClientChannelSubchannelNode::PopulateTarget(grpc_json* json) {
+ GPR_ASSERT(target_.get() != nullptr);
+ grpc_json_create_child(nullptr, json, "target", target_.get(),
+ GRPC_JSON_STRING, false);
+}
+
+void ClientChannelSubchannelNode::PopulateConnectivityState(grpc_json* json) {
+ grpc_connectivity_state state;
+ if (subchannel_ == nullptr) {
+ state = GRPC_CHANNEL_SHUTDOWN;
+ } else {
+ state = grpc_subchannel_check_connectivity(subchannel_, nullptr);
+ }
+ 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);
+}
+
} // namespace channelz
} // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/client_channel_channelz.h b/src/core/ext/filters/client_channel/client_channel_channelz.h
index 6f27b5c8b7..b1bd8db89e 100644
--- a/src/core/ext/filters/client_channel/client_channel_channelz.h
+++ b/src/core/ext/filters/client_channel/client_channel_channelz.h
@@ -26,6 +26,8 @@
#include "src/core/lib/channel/channelz.h"
#include "src/core/lib/gprpp/inlined_vector.h"
+typedef struct grpc_subchannel grpc_subchannel;
+
namespace grpc_core {
// TODO(ncteisen), this only contains the uuids of the children for now,
@@ -55,16 +57,45 @@ class ClientChannelNode : public ChannelNode {
static grpc_arg CreateChannelArg();
protected:
- GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
- GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
ClientChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel);
virtual ~ClientChannelNode() {}
private:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
grpc_channel_element* client_channel_;
};
+// Subtype of SubchannelNode that overrides and provides client_channel
+// specific functionality like querying for connectivity_state and
+// subchannel target.
+class ClientChannelSubchannelNode : public SubchannelNode {
+ public:
+ ClientChannelSubchannelNode(size_t channel_tracer_max_nodes,
+ grpc_subchannel* subchannel);
+ ~ClientChannelSubchannelNode() override {}
+
+ // Override this functionality since subchannels have a notion of
+ // channel connectivity.
+ void PopulateConnectivityState(grpc_json* json) override;
+
+ // Override this functionality since client_channels subchannels hold
+ // their own target.
+ void PopulateTarget(grpc_json* json) override;
+
+ void MarkSubchannelDestroyed() {
+ GPR_ASSERT(subchannel_ != nullptr);
+ subchannel_ = nullptr;
+ }
+
+ private:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+ grpc_subchannel* subchannel_;
+ UniquePtr<char> target_;
+};
+
} // namespace channelz
} // namespace grpc_core
diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc
index 93df2aff70..0c3313c5de 100644
--- a/src/core/ext/filters/client_channel/subchannel.cc
+++ b/src/core/ext/filters/client_channel/subchannel.cc
@@ -135,7 +135,7 @@ struct grpc_subchannel {
/** our alarm */
grpc_timer alarm;
- grpc_core::RefCountedPtr<grpc_core::channelz::SubchannelNode>
+ grpc_core::RefCountedPtr<grpc_core::channelz::ClientChannelSubchannelNode>
channelz_subchannel;
};
@@ -181,7 +181,13 @@ static void connection_destroy(void* arg, grpc_error* error) {
static void subchannel_destroy(void* arg, grpc_error* error) {
grpc_subchannel* c = static_cast<grpc_subchannel*>(arg);
- c->channelz_subchannel.reset();
+ if (c->channelz_subchannel != nullptr) {
+ c->channelz_subchannel->trace()->AddTraceEvent(
+ grpc_core::channelz::ChannelTrace::Severity::Info,
+ grpc_slice_from_static_string("Subchannel destroyed"));
+ c->channelz_subchannel->MarkSubchannelDestroyed();
+ c->channelz_subchannel.reset();
+ }
gpr_free((void*)c->filters);
grpc_channel_args_destroy(c->args);
grpc_connectivity_state_destroy(&c->state_tracker);
@@ -381,9 +387,18 @@ grpc_subchannel* grpc_subchannel_create(grpc_connector* connector,
const grpc_arg* arg =
grpc_channel_args_find(c->args, GRPC_ARG_ENABLE_CHANNELZ);
bool channelz_enabled = grpc_channel_arg_get_bool(arg, false);
+ arg = grpc_channel_args_find(c->args,
+ GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE);
+ const grpc_integer_options options = {0, 0, INT_MAX};
+ size_t channel_tracer_max_nodes =
+ (size_t)grpc_channel_arg_get_integer(arg, options);
if (channelz_enabled) {
- c->channelz_subchannel =
- grpc_core::MakeRefCounted<grpc_core::channelz::SubchannelNode>();
+ c->channelz_subchannel = grpc_core::MakeRefCounted<
+ grpc_core::channelz::ClientChannelSubchannelNode>(
+ channel_tracer_max_nodes, c);
+ c->channelz_subchannel->trace()->AddTraceEvent(
+ grpc_core::channelz::ChannelTrace::Severity::Info,
+ grpc_slice_from_static_string("Subchannel created"));
}
return grpc_subchannel_index_register(key, c);
@@ -757,6 +772,14 @@ void grpc_get_subchannel_address_arg(const grpc_channel_args* args,
}
}
+const char* grpc_subchannel_get_target(grpc_subchannel* subchannel) {
+ const grpc_arg* addr_arg =
+ grpc_channel_args_find(subchannel->args, GRPC_ARG_SUBCHANNEL_ADDRESS);
+ const char* addr_str = grpc_channel_arg_get_string(addr_arg);
+ GPR_ASSERT(addr_str != nullptr); // Should have been set by LB policy.
+ return addr_str;
+}
+
const char* grpc_get_subchannel_address_uri_arg(const grpc_channel_args* args) {
const grpc_arg* addr_arg =
grpc_channel_args_find(args, GRPC_ARG_SUBCHANNEL_ADDRESS);
diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h
index 9e53f7d542..dd3a2d9621 100644
--- a/src/core/ext/filters/client_channel/subchannel.h
+++ b/src/core/ext/filters/client_channel/subchannel.h
@@ -177,6 +177,8 @@ grpc_subchannel* grpc_subchannel_create(grpc_connector* connector,
void grpc_get_subchannel_address_arg(const grpc_channel_args* args,
grpc_resolved_address* addr);
+const char* grpc_subchannel_get_target(grpc_subchannel* subchannel);
+
/// Returns the URI string for the address to connect to.
const char* grpc_get_subchannel_address_uri_arg(const grpc_channel_args* args);
diff --git a/src/core/lib/channel/channel_trace.cc b/src/core/lib/channel/channel_trace.cc
index b3443310ac..dd6fdeed54 100644
--- a/src/core/lib/channel/channel_trace.cc
+++ b/src/core/lib/channel/channel_trace.cc
@@ -178,24 +178,26 @@ grpc_json* ChannelTrace::RenderJson() const {
if (!max_list_size_)
return nullptr; // tracing is disabled if max_events == 0
grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT);
- char* num_events_logged_str;
- gpr_asprintf(&num_events_logged_str, "%" PRId64, num_events_logged_);
grpc_json* json_iterator = nullptr;
- json_iterator =
- grpc_json_create_child(json_iterator, json, "numEventsLogged",
- num_events_logged_str, GRPC_JSON_STRING, true);
+ if (num_events_logged_ > 0) {
+ json_iterator = grpc_json_add_number_string_child(
+ json, json_iterator, "numEventsLogged", num_events_logged_);
+ }
json_iterator = grpc_json_create_child(
json_iterator, json, "creationTimestamp",
gpr_format_timespec(time_created_), GRPC_JSON_STRING, true);
- grpc_json* events = grpc_json_create_child(json_iterator, json, "events",
- nullptr, GRPC_JSON_ARRAY, false);
- json_iterator = nullptr;
- TraceEvent* it = head_trace_;
- while (it != nullptr) {
- json_iterator = grpc_json_create_child(json_iterator, events, nullptr,
- nullptr, GRPC_JSON_OBJECT, false);
- it->RenderTraceEvent(json_iterator);
- it = it->next();
+ // only add in the event list if it is non-empty.
+ if (num_events_logged_ > 0) {
+ grpc_json* events = grpc_json_create_child(json_iterator, json, "events",
+ nullptr, GRPC_JSON_ARRAY, false);
+ json_iterator = nullptr;
+ TraceEvent* it = head_trace_;
+ while (it != nullptr) {
+ json_iterator = grpc_json_create_child(json_iterator, events, nullptr,
+ nullptr, GRPC_JSON_OBJECT, false);
+ it->RenderTraceEvent(json_iterator);
+ it = it->next();
+ }
}
return json;
}
diff --git a/src/core/lib/channel/channelz.cc b/src/core/lib/channel/channelz.cc
index 9d6002ed8a..7fe086da3c 100644
--- a/src/core/lib/channel/channelz.cc
+++ b/src/core/lib/channel/channelz.cc
@@ -41,69 +41,33 @@
namespace grpc_core {
namespace channelz {
-ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
- bool is_top_level_channel)
- : channel_(channel),
- target_(nullptr),
- channel_uuid_(-1),
- is_top_level_channel_(is_top_level_channel) {
+CallCountingBase::CallCountingBase(size_t channel_tracer_max_nodes) {
trace_.Init(channel_tracer_max_nodes);
- target_ = UniquePtr<char>(grpc_channel_get_target(channel_));
- channel_uuid_ = ChannelzRegistry::RegisterChannelNode(this);
gpr_atm_no_barrier_store(&last_call_started_millis_,
(gpr_atm)ExecCtx::Get()->Now());
}
-ChannelNode::~ChannelNode() {
- trace_.Destroy();
- ChannelzRegistry::UnregisterChannelNode(channel_uuid_);
-}
+CallCountingBase::~CallCountingBase() { trace_.Destroy(); }
-void ChannelNode::RecordCallStarted() {
+void CallCountingBase::RecordCallStarted() {
gpr_atm_no_barrier_fetch_add(&calls_started_, (gpr_atm)1);
gpr_atm_no_barrier_store(&last_call_started_millis_,
(gpr_atm)ExecCtx::Get()->Now());
}
-void ChannelNode::PopulateConnectivityState(grpc_json* json) {}
-
-void ChannelNode::PopulateChildRefs(grpc_json* json) {}
-
-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", channel_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;
- PopulateConnectivityState(json);
- GPR_ASSERT(target_.get() != nullptr);
- json_iterator = grpc_json_create_child(
- json_iterator, json, "target", target_.get(), GRPC_JSON_STRING, false);
+void CallCountingBase::PopulateTrace(grpc_json* json) {
// fill in the channel trace if applicable
- grpc_json* trace = trace_->RenderJson();
- if (trace != nullptr) {
+ grpc_json* trace_json = trace_->RenderJson();
+ if (trace_json != nullptr) {
// we manually link up and fill the child since it was created for us in
// ChannelTrace::RenderJson
- trace->key = "trace"; // this object is named trace in channelz.proto
- json_iterator = grpc_json_link_child(json, trace, json_iterator);
+ trace_json->key = "trace"; // this object is named trace in channelz.proto
+ grpc_json_link_child(json, trace_json, nullptr);
}
- // reset the parent to be the data object.
- json = data;
- json_iterator = nullptr;
+}
+
+void CallCountingBase::PopulateCallData(grpc_json* json) {
+ grpc_json* json_iterator = nullptr;
if (calls_started_ != 0) {
json_iterator = grpc_json_add_number_string_child(
json, json_iterator, "callsStarted", calls_started_);
@@ -121,19 +85,62 @@ grpc_json* ChannelNode::RenderJson() {
json_iterator =
grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp",
gpr_format_timespec(ts), GRPC_JSON_STRING, true);
- json = top_level_json;
- json_iterator = nullptr;
- PopulateChildRefs(json);
- return top_level_json;
}
-char* ChannelNode::RenderJsonString() {
+char* CallCountingBase::RenderJsonString() {
grpc_json* json = RenderJson();
char* json_str = grpc_json_dump_to_string(json, 0);
grpc_json_destroy(json);
return json_str;
}
+ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
+ bool is_top_level_channel)
+ : CallCountingBase(channel_tracer_max_nodes),
+ channel_(channel),
+ is_top_level_channel_(is_top_level_channel) {
+ target_ = UniquePtr<char>(grpc_channel_get_target(channel_));
+ channel_uuid_ = ChannelzRegistry::RegisterChannelNode(this);
+}
+
+ChannelNode::~ChannelNode() {
+ ChannelzRegistry::UnregisterChannelNode(channel_uuid_);
+}
+
+void ChannelNode::PopulateTarget(grpc_json* json) {
+ GPR_ASSERT(target_.get() != nullptr);
+ grpc_json_create_child(nullptr, json, "target", target_.get(),
+ GRPC_JSON_STRING, false);
+}
+
+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", channel_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;
+ PopulateConnectivityState(json);
+ PopulateTarget(json);
+ PopulateTrace(json);
+ PopulateCallData(json);
+ PopulateChildRefs(json);
+ return top_level_json;
+}
+
RefCountedPtr<ChannelNode> ChannelNode::MakeChannelNode(
grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel) {
@@ -141,7 +148,8 @@ RefCountedPtr<ChannelNode> ChannelNode::MakeChannelNode(
channel, channel_tracer_max_nodes, is_top_level_channel);
}
-SubchannelNode::SubchannelNode() {
+SubchannelNode::SubchannelNode(size_t channel_tracer_max_nodes)
+ : CallCountingBase(channel_tracer_max_nodes) {
subchannel_uuid_ = ChannelzRegistry::RegisterSubchannelNode(this);
}
@@ -149,5 +157,31 @@ SubchannelNode::~SubchannelNode() {
ChannelzRegistry::UnregisterSubchannelNode(subchannel_uuid_);
}
+grpc_json* SubchannelNode::RenderJson() {
+ grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+ grpc_json* json = top_level_json;
+ grpc_json* json_iterator = nullptr;
+ 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, "subchannelId", subchannel_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;
+ PopulateConnectivityState(json);
+ PopulateTarget(json);
+ PopulateTrace(json);
+ PopulateCallData(json);
+ PopulateChildRefs(json);
+ return top_level_json;
+}
+
} // namespace channelz
} // namespace grpc_core
diff --git a/src/core/lib/channel/channelz.h b/src/core/lib/channel/channelz.h
index 07eb73d626..97e27c69fc 100644
--- a/src/core/lib/channel/channelz.h
+++ b/src/core/lib/channel/channelz.h
@@ -46,11 +46,44 @@ namespace testing {
class ChannelNodePeer;
}
-class ChannelNode : public RefCounted<ChannelNode> {
+// base class for all channelz entities
+class ChannelzBaseNode : public RefCounted<ChannelzBaseNode> {
public:
- static RefCountedPtr<ChannelNode> MakeChannelNode(
- grpc_channel* channel, size_t channel_tracer_max_nodes,
- bool is_top_level_channel);
+ ChannelzBaseNode() {}
+ virtual ~ChannelzBaseNode() {}
+ private:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+};
+
+// Handles channelz bookkeeping for sockets
+// TODO(ncteisen): implement in subsequent PR.
+class SocketNode : public ChannelzBaseNode {
+ public:
+ SocketNode() : ChannelzBaseNode() {}
+ ~SocketNode() override {}
+ private:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+};
+
+// This class is the parent for the channelz entities that deal with Channels
+// Subchannels, and Servers, since those have similar proto definitions.
+// This class has the ability to:
+// - track calls_{started,succeeded,failed}
+// - track last_call_started_timestamp
+// - hold the channel trace.
+// - perform common rendering.
+//
+// This class also defines some fat interfaces so that its children can
+// implement the functionality different. For example, querying the
+// connectivity state looks different for channels and subchannels, and does
+// not make sense for servers. So servers will not override, and channels and
+// subchannels will override with their own way to query connectivity state.
+class CallCountingBase : public ChannelzBaseNode {
+ public:
+ CallCountingBase(size_t channel_tracer_max_nodes);
+ ~CallCountingBase() override;
void RecordCallStarted();
void RecordCallFailed() {
@@ -59,66 +92,106 @@ class ChannelNode : public RefCounted<ChannelNode> {
void RecordCallSucceeded() {
gpr_atm_no_barrier_fetch_add(&calls_succeeded_, (gpr_atm(1)));
}
+ ChannelTrace* trace() { return trace_.get(); }
+
+ // Fat interface for ConnectivityState. Default is to leave it out, however,
+ // things like Channel and Subchannel will override with their mechanism
+ // for querying connectivity state.
+ virtual void PopulateConnectivityState(grpc_json* json) {}
+
+ // Fat interface for Targets.
+ virtual void PopulateTarget(grpc_json* json) {}
+
+ // Fat interface for ChildRefs. Allows children to populate with whatever
+ // combination of child_refs, subchannel_refs, and socket_refs is correct.
+ virtual void PopulateChildRefs(grpc_json* json) {}
- grpc_json* RenderJson();
+ // All children must implement their custom JSON rendering.
+ virtual grpc_json* RenderJson() GRPC_ABSTRACT;
+
+ // Common rendering of the channel trace.
+ void PopulateTrace(grpc_json* json);
+
+ // Common rendering of the call count data and last_call_started_timestamp.
+ void PopulateCallData(grpc_json* json);
+
+ // Common rendering of grpc_json from RenderJson() to allocated string.
char* RenderJsonString();
- // helper for getting and populating connectivity state. It is virtual
- // because it allows the client_channel specific code to live in ext/
- // instead of lib/
- virtual void PopulateConnectivityState(grpc_json* json);
+ private:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
- virtual void PopulateChildRefs(grpc_json* json);
+ gpr_atm calls_started_ = 0;
+ gpr_atm calls_succeeded_ = 0;
+ gpr_atm calls_failed_ = 0;
+ gpr_atm last_call_started_millis_ = 0;
+ ManualConstructor<ChannelTrace> trace_;
+};
- ChannelTrace* trace() { return trace_.get(); }
+// Handles channelz bookkeeping for servers
+// TODO(ncteisen): implement in subsequent PR.
+class ServerNode : public CallCountingBase {
+ public:
+ ServerNode(size_t channel_tracer_max_nodes)
+ : CallCountingBase(channel_tracer_max_nodes) {}
+ ~ServerNode() override {}
+ private:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+};
+
+// Overrides Channel specific functionality.
+class ChannelNode : public CallCountingBase {
+ public:
+ static RefCountedPtr<ChannelNode> MakeChannelNode(
+ grpc_channel* channel, size_t channel_tracer_max_nodes,
+ bool is_top_level_channel);
void MarkChannelDestroyed() {
GPR_ASSERT(channel_ != nullptr);
channel_ = nullptr;
}
+ grpc_json* RenderJson() override;
+
+ void PopulateTarget(grpc_json* json) override;
+
bool ChannelIsDestroyed() { return channel_ == nullptr; }
intptr_t channel_uuid() { return channel_uuid_; }
bool is_top_level_channel() { return is_top_level_channel_; }
protected:
- GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
- GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes,
bool is_top_level_channel);
- virtual ~ChannelNode();
+ ~ChannelNode() override;
private:
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
+ GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
+
// testing peer friend.
friend class testing::ChannelNodePeer;
grpc_channel* channel_ = nullptr;
UniquePtr<char> target_;
- gpr_atm calls_started_ = 0;
- gpr_atm calls_succeeded_ = 0;
- gpr_atm calls_failed_ = 0;
- gpr_atm last_call_started_millis_ = 0;
intptr_t channel_uuid_;
bool is_top_level_channel_ = true;
- ManualConstructor<ChannelTrace> trace_;
};
-// Placeholds channelz class for subchannels. All this can do now is track its
-// uuid (this information is needed by the parent channelz class).
-// TODO(ncteisen): build this out to support the GetSubchannel channelz request.
-class SubchannelNode : public RefCounted<SubchannelNode> {
+// Overrides Subchannel specific functionality.
+class SubchannelNode : public CallCountingBase {
public:
- SubchannelNode();
- virtual ~SubchannelNode();
-
+ SubchannelNode(size_t channel_tracer_max_nodes);
+ ~SubchannelNode() override;
+ grpc_json* RenderJson() override;
intptr_t subchannel_uuid() { return subchannel_uuid_; }
- protected:
+ private:
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW
-
- private:
+
intptr_t subchannel_uuid_;
};
diff --git a/src/core/lib/channel/channelz_registry.cc b/src/core/lib/channel/channelz_registry.cc
index 38496b3d78..aeeb6958e4 100644
--- a/src/core/lib/channel/channelz_registry.cc
+++ b/src/core/lib/channel/channelz_registry.cc
@@ -142,3 +142,19 @@ char* grpc_channelz_get_channel(intptr_t channel_id) {
grpc_json_destroy(top_level_json);
return json_str;
}
+
+char* grpc_channelz_get_subchannel(intptr_t subchannel_id) {
+ grpc_core::channelz::SubchannelNode* subchannel_node =
+ grpc_core::channelz::ChannelzRegistry::GetSubchannelNode(subchannel_id);
+ if (subchannel_node == nullptr) {
+ return nullptr;
+ }
+ grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT);
+ grpc_json* json = top_level_json;
+ grpc_json* subchannel_json = subchannel_node->RenderJson();
+ subchannel_json->key = "subchannel";
+ grpc_json_link_child(json, subchannel_json, nullptr);
+ char* json_str = grpc_json_dump_to_string(top_level_json, 0);
+ grpc_json_destroy(top_level_json);
+ return json_str;
+}
diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc
index 7cbd61adef..01caadaaba 100644
--- a/src/core/lib/surface/channel.cc
+++ b/src/core/lib/surface/channel.cc
@@ -417,6 +417,9 @@ void grpc_channel_internal_unref(grpc_channel* c REF_ARG) {
static void destroy_channel(void* arg, grpc_error* error) {
grpc_channel* channel = static_cast<grpc_channel*>(arg);
if (channel->channelz_channel != nullptr) {
+ channel->channelz_channel->trace()->AddTraceEvent(
+ grpc_core::channelz::ChannelTrace::Severity::Info,
+ grpc_slice_from_static_string("Channel destroyed"));
channel->channelz_channel->MarkChannelDestroyed();
channel->channelz_channel.reset();
}
diff --git a/test/core/channel/channelz_test.cc b/test/core/channel/channelz_test.cc
index ad5f86d934..b8e65ebfb3 100644
--- a/test/core/channel/channelz_test.cc
+++ b/test/core/channel/channelz_test.cc
@@ -163,6 +163,14 @@ void ValidateChannel(ChannelNode* channel, validate_channel_data_args args) {
gpr_free(core_api_json_str);
}
+void ValidateSubchannel(SubchannelNode* subchannel,
+ validate_channel_data_args args) {
+ char* json_str = subchannel->RenderJsonString();
+ grpc::testing::ValidateSubchannelProtoJsonTranslation(json_str);
+ ValidateCounters(json_str, args);
+ gpr_free(json_str);
+}
+
grpc_millis GetLastCallStartedMillis(ChannelNode* channel) {
ChannelNodePeer peer(channel);
return peer.last_call_started_millis();
@@ -275,8 +283,29 @@ TEST(ChannelzGetTopChannelsTest, InternalChannelTest) {
grpc_channel_destroy(internal_channel);
}
+class ChannelzSubchannelTest : public ::testing::TestWithParam<size_t> {};
+
+TEST_P(ChannelzSubchannelTest, BasicTest) {
+ grpc_core::ExecCtx exec_ctx;
+ RefCountedPtr<SubchannelNode> channelz_subchannel =
+ MakeRefCounted<SubchannelNode>(GetParam());
+ channelz_subchannel->RecordCallStarted();
+ channelz_subchannel->RecordCallFailed();
+ channelz_subchannel->RecordCallSucceeded();
+ ValidateSubchannel(channelz_subchannel.get(), {1, 1, 1});
+ channelz_subchannel->RecordCallStarted();
+ channelz_subchannel->RecordCallFailed();
+ channelz_subchannel->RecordCallSucceeded();
+ channelz_subchannel->RecordCallStarted();
+ channelz_subchannel->RecordCallFailed();
+ channelz_subchannel->RecordCallSucceeded();
+ ValidateSubchannel(channelz_subchannel.get(), {3, 3, 3});
+}
+
INSTANTIATE_TEST_CASE_P(ChannelzChannelTestSweep, ChannelzChannelTest,
::testing::Values(0, 1, 2, 6, 10, 15));
+INSTANTIATE_TEST_CASE_P(ChannelzSubchannelTestSweep, ChannelzSubchannelTest,
+ ::testing::Values(0, 1, 10, 15));
} // namespace testing
} // namespace channelz
diff --git a/test/cpp/util/channel_trace_proto_helper.cc b/test/cpp/util/channel_trace_proto_helper.cc
index b4704bfe6a..e416a0375f 100644
--- a/test/cpp/util/channel_trace_proto_helper.cc
+++ b/test/cpp/util/channel_trace_proto_helper.cc
@@ -82,5 +82,9 @@ void ValidateGetChannelResponseProtoJsonTranslation(char* json_c_str) {
json_c_str);
}
+void ValidateSubchannelProtoJsonTranslation(char* json_c_str) {
+ VaidateProtoJsonTranslation<grpc::channelz::v1::Subchannel>(json_c_str);
+}
+
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/util/channel_trace_proto_helper.h b/test/cpp/util/channel_trace_proto_helper.h
index 18e3d54b6b..a1ebbcd110 100644
--- a/test/cpp/util/channel_trace_proto_helper.h
+++ b/test/cpp/util/channel_trace_proto_helper.h
@@ -26,6 +26,7 @@ void ValidateChannelTraceProtoJsonTranslation(char* json_c_str);
void ValidateChannelProtoJsonTranslation(char* json_c_str);
void ValidateGetTopChannelsResponseProtoJsonTranslation(char* json_c_str);
void ValidateGetChannelResponseProtoJsonTranslation(char* json_c_str);
+void ValidateSubchannelProtoJsonTranslation(char* json_c_str);
} // namespace testing
} // namespace grpc