diff options
author | ncteisen <ncteisen@gmail.com> | 2018-07-18 18:31:09 -0700 |
---|---|---|
committer | ncteisen <ncteisen@gmail.com> | 2018-07-19 08:15:49 -0700 |
commit | bbee13661c82c13e2f1b728c94f9535112f54d92 (patch) | |
tree | ed60505ea44cdde179e837ff13050445afe7809b | |
parent | 481c1d57e7fc92f74c79b9aa0f7c8f0eaa51000b (diff) |
Add channelz subchannel support
-rw-r--r-- | include/grpc/grpc.h | 4 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/client_channel_channelz.cc | 29 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/client_channel_channelz.h | 35 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/subchannel.cc | 31 | ||||
-rw-r--r-- | src/core/ext/filters/client_channel/subchannel.h | 2 | ||||
-rw-r--r-- | src/core/lib/channel/channel_trace.cc | 30 | ||||
-rw-r--r-- | src/core/lib/channel/channelz.cc | 142 | ||||
-rw-r--r-- | src/core/lib/channel/channelz.h | 131 | ||||
-rw-r--r-- | src/core/lib/channel/channelz_registry.cc | 16 | ||||
-rw-r--r-- | src/core/lib/surface/channel.cc | 3 | ||||
-rw-r--r-- | test/core/channel/channelz_test.cc | 29 | ||||
-rw-r--r-- | test/cpp/util/channel_trace_proto_helper.cc | 4 | ||||
-rw-r--r-- | test/cpp/util/channel_trace_proto_helper.h | 1 |
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 |