Add channelz subchannel support

reviewable/pr16055/r1
ncteisen 7 years ago
parent 481c1d57e7
commit bbee13661c
  1. 4
      include/grpc/grpc.h
  2. 29
      src/core/ext/filters/client_channel/client_channel_channelz.cc
  3. 35
      src/core/ext/filters/client_channel/client_channel_channelz.h
  4. 31
      src/core/ext/filters/client_channel/subchannel.cc
  5. 2
      src/core/ext/filters/client_channel/subchannel.h
  6. 30
      src/core/lib/channel/channel_trace.cc
  7. 142
      src/core/lib/channel/channelz.cc
  8. 131
      src/core/lib/channel/channelz.h
  9. 16
      src/core/lib/channel/channelz_registry.cc
  10. 3
      src/core/lib/surface/channel.cc
  11. 29
      test/core/channel/channelz_test.cc
  12. 4
      test/cpp/util/channel_trace_proto_helper.cc
  13. 1
      test/cpp/util/channel_trace_proto_helper.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

@ -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

@ -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

@ -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);

@ -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);

@ -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;
}

@ -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

@ -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_;
};

@ -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;
}

@ -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();
}

@ -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

@ -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

@ -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

Loading…
Cancel
Save