diff options
-rw-r--r-- | src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc | 3 | ||||
-rw-r--r-- | src/core/lib/channel/channel_trace.cc | 2 | ||||
-rw-r--r-- | src/core/lib/channel/channel_trace.h | 2 | ||||
-rw-r--r-- | src/core/lib/channel/channelz.cc | 37 | ||||
-rw-r--r-- | src/core/lib/channel/channelz.h | 12 | ||||
-rw-r--r-- | src/core/lib/channel/channelz_registry.cc | 45 | ||||
-rw-r--r-- | src/core/lib/channel/channelz_registry.h | 7 | ||||
-rw-r--r-- | src/core/lib/surface/channel.cc | 7 | ||||
-rw-r--r-- | test/core/channel/channel_trace_test.cc | 2 | ||||
-rw-r--r-- | test/core/channel/channelz_test.cc | 81 | ||||
-rw-r--r-- | test/core/end2end/tests/channelz.cc | 16 | ||||
-rw-r--r-- | test/cpp/util/channel_trace_proto_helper.cc | 14 | ||||
-rw-r--r-- | test/cpp/util/channel_trace_proto_helper.h | 5 |
13 files changed, 195 insertions, 38 deletions
diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc index 263b51ae89..01d05dbb14 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -1004,6 +1004,9 @@ grpc_channel_args* BuildBalancerChannelArgs( // A channel arg indicating the target is a grpclb load balancer. grpc_channel_arg_integer_create( const_cast<char*>(GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER), 1), + // A channel arg indicating the target is a grpclb load balancer. + grpc_channel_arg_integer_create( + const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), 1), }; // Construct channel args. grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove( diff --git a/src/core/lib/channel/channel_trace.cc b/src/core/lib/channel/channel_trace.cc index 0f655d8716..5a4cf8c7e1 100644 --- a/src/core/lib/channel/channel_trace.cc +++ b/src/core/lib/channel/channel_trace.cc @@ -206,7 +206,7 @@ void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const { } } -grpc_json* ChannelTrace::RenderJSON() const { +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); diff --git a/src/core/lib/channel/channel_trace.h b/src/core/lib/channel/channel_trace.h index 0dd162a777..596af7402f 100644 --- a/src/core/lib/channel/channel_trace.h +++ b/src/core/lib/channel/channel_trace.h @@ -71,7 +71,7 @@ class ChannelTrace { // Creates and returns the raw grpc_json object, so a parent channelz // object may incorporate the json before rendering. - grpc_json* RenderJSON() const; + grpc_json* RenderJson() const; private: // Types of objects that can be references by trace events. diff --git a/src/core/lib/channel/channelz.cc b/src/core/lib/channel/channelz.cc index 923b8043cd..c8a1d179fa 100644 --- a/src/core/lib/channel/channelz.cc +++ b/src/core/lib/channel/channelz.cc @@ -110,7 +110,7 @@ void ChannelNode::RecordCallStarted() { void ChannelNode::PopulateConnectivityState(grpc_json* json) {} -char* ChannelNode::RenderJSON() { +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; @@ -130,13 +130,14 @@ char* ChannelNode::RenderJSON() { 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); // fill in the channel trace if applicable - grpc_json* trace = trace_->RenderJSON(); + grpc_json* trace = trace_->RenderJson(); if (trace != nullptr) { // we manuall link up and fill the child since it was created for us in - // ChannelTrace::RenderJSON + // ChannelTrace::RenderJson json_iterator = grpc_json_link_child(json, trace, json_iterator); trace->parent = json; trace->value = nullptr; @@ -146,22 +147,30 @@ char* ChannelNode::RenderJSON() { // reset the parent to be the data object. json = data; json_iterator = nullptr; - // We use -1 as sentinel values since proto default value for integers is - // zero, and the confuses the parser into thinking the value weren't present - json_iterator = - add_num_str(json, json_iterator, "callsStarted", calls_started_); - json_iterator = - add_num_str(json, json_iterator, "callsSucceeded", calls_succeeded_); - json_iterator = - add_num_str(json, json_iterator, "callsFailed", calls_failed_); + if (calls_started_ != 0) { + json_iterator = + add_num_str(json, json_iterator, "callsStarted", calls_started_); + } + if (calls_succeeded_ != 0) { + json_iterator = + add_num_str(json, json_iterator, "callsSucceeded", calls_succeeded_); + } + if (calls_failed_) { + json_iterator = + add_num_str(json, json_iterator, "callsFailed", calls_failed_); + } gpr_timespec ts = grpc_millis_to_timespec(last_call_started_millis_, GPR_CLOCK_REALTIME); json_iterator = grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp", fmt_time(ts), GRPC_JSON_STRING, true); - // render and return the over json object - char* json_str = grpc_json_dump_to_string(top_level_json, 0); - grpc_json_destroy(top_level_json); + return top_level_json; +} + +char* ChannelNode::RenderJsonString() { + grpc_json* json = RenderJson(); + char* json_str = grpc_json_dump_to_string(json, 0); + grpc_json_destroy(json); return json_str; } diff --git a/src/core/lib/channel/channelz.h b/src/core/lib/channel/channelz.h index 9bd01ece50..abcf907c2b 100644 --- a/src/core/lib/channel/channelz.h +++ b/src/core/lib/channel/channelz.h @@ -35,6 +35,10 @@ #define GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC \ "grpc.channelz_channel_node_creation_func" +// Channel arg key to signal that the channel is an internal channel. +#define GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL \ + "grpc.channelz_channel_is_internal_channel" + namespace grpc_core { namespace channelz { @@ -55,7 +59,8 @@ class ChannelNode : public RefCounted<ChannelNode> { gpr_atm_no_barrier_fetch_add(&calls_succeeded_, (gpr_atm(1))); } - char* RenderJSON(); + grpc_json* RenderJson(); + char* RenderJsonString(); // helper for getting and populating connectivity state. It is virtual // because it allows the client_channel specific code to live in ext/ @@ -72,6 +77,10 @@ class ChannelNode : public RefCounted<ChannelNode> { bool ChannelIsDestroyed() { return channel_ == nullptr; } intptr_t channel_uuid() { return channel_uuid_; } + bool is_top_level_channel() { return is_top_level_channel_; } + void set_is_top_level_channel(bool is_top_level_channel) { + is_top_level_channel_ = is_top_level_channel; + } protected: GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE @@ -90,6 +99,7 @@ class ChannelNode : public RefCounted<ChannelNode> { 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_; }; diff --git a/src/core/lib/channel/channelz_registry.cc b/src/core/lib/channel/channelz_registry.cc index 43cace1975..b4dc3d1103 100644 --- a/src/core/lib/channel/channelz_registry.cc +++ b/src/core/lib/channel/channelz_registry.cc @@ -19,6 +19,7 @@ #include <grpc/impl/codegen/port_platform.h> #include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/channel/channelz.h" #include "src/core/lib/channel/channelz_registry.h" #include "src/core/lib/gpr/useful.h" #include "src/core/lib/gprpp/memory.h" @@ -64,6 +65,7 @@ void ChannelzRegistry::InternalUnregisterEntry(intptr_t uuid, EntityType type) { GPR_ASSERT(static_cast<size_t>(uuid) <= entities_.size()); GPR_ASSERT(entities_[uuid - 1].type == type); entities_[uuid - 1].object = nullptr; + entities_[uuid - 1].type = EntityType::kUnset; } void* ChannelzRegistry::InternalGetEntry(intptr_t uuid, EntityType type) { @@ -78,5 +80,48 @@ void* ChannelzRegistry::InternalGetEntry(intptr_t uuid, EntityType type) { } } +char* ChannelzRegistry::InternalGetTopChannels(intptr_t start_channel_id) { + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + InlinedVector<ChannelNode*, 10> top_level_channels; + // uuids index into entities one-off (idx 0 is really uuid 1, since 0 is + // reserver). However, we want to support requests coming in which + // start_channel_id=0, which signifies "give me everything." Hence this + // funky looking line below. + size_t start_idx = start_channel_id == 0 ? 0 : start_channel_id - 1; + for (size_t i = start_idx; i < entities_.size(); ++i) { + if (entities_[i].type == EntityType::kChannelNode) { + ChannelNode* channel_node = + static_cast<ChannelNode*>(entities_[i].object); + if (channel_node->is_top_level_channel()) { + top_level_channels.push_back(channel_node); + } + } + } + if (top_level_channels.size() > 0) { + // create list of channels + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "channel", nullptr, GRPC_JSON_ARRAY, false); + for (size_t i = 0; i < top_level_channels.size(); ++i) { + grpc_json* channel_json = top_level_channels[i]->RenderJson(); + json_iterator = + grpc_json_link_child(array_parent, channel_json, json_iterator); + channel_json->parent = array_parent; + channel_json->value = nullptr; + channel_json->key = nullptr; + channel_json->owns_value = false; + } + } + // For now we do not have any pagination rules. In the future we could + // pick a constant for max_channels_sent for a GetTopChannels request. + // Tracking: https://github.com/grpc/grpc/issues/16019. + 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; +} + } // namespace channelz } // namespace grpc_core diff --git a/src/core/lib/channel/channelz_registry.h b/src/core/lib/channel/channelz_registry.h index cc069d9145..2fefc9f629 100644 --- a/src/core/lib/channel/channelz_registry.h +++ b/src/core/lib/channel/channelz_registry.h @@ -51,6 +51,11 @@ class ChannelzRegistry { return gotten == nullptr ? nullptr : static_cast<ChannelNode*>(gotten); } + // todo, protect me + static char* GetTopChannels(intptr_t start_channel_id) { + return Default()->InternalGetTopChannels(start_channel_id); + } + private: enum class EntityType { kChannelNode, @@ -84,6 +89,8 @@ class ChannelzRegistry { // returns the void* associated with that uuid. Else returns nullptr. void* InternalGetEntry(intptr_t uuid, EntityType type); + char* InternalGetTopChannels(intptr_t start_channel_id); + // protects entities_ and uuid_ gpr_mu mu_; InlinedVector<RegistryEntry, 20> entities_; diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc index 8f3ad6c191..dd3880f18e 100644 --- a/src/core/lib/surface/channel.cc +++ b/src/core/lib/surface/channel.cc @@ -105,6 +105,7 @@ grpc_channel* grpc_channel_create_with_builder( channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type); size_t channel_tracer_max_nodes = 0; // default to off bool channelz_enabled = false; + bool internal_channel = false; // this creates the default ChannelNode. Different types of channels may // override this to ensure a correct ChannelNode is created. grpc_core::channelz::ChannelNodeCreationFunc channel_node_create_func = @@ -158,6 +159,9 @@ grpc_channel* grpc_channel_create_with_builder( channel_node_create_func = reinterpret_cast<grpc_core::channelz::ChannelNodeCreationFunc>( args->args[i].value.pointer.p); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL)) { + internal_channel = grpc_channel_arg_get_bool(&args->args[i], false); } } @@ -165,6 +169,9 @@ grpc_channel* grpc_channel_create_with_builder( if (channelz_enabled) { channel->channelz_channel = channel_node_create_func(channel, channel_tracer_max_nodes); + if (internal_channel || !channel->is_client) { + channel->channelz_channel->set_is_top_level_channel(false); + } channel->channelz_channel->trace()->AddTraceEvent( grpc_core::channelz::ChannelTrace::Severity::Info, grpc_slice_from_static_string("Channel created")); diff --git a/test/core/channel/channel_trace_test.cc b/test/core/channel/channel_trace_test.cc index bbddee3f14..07b0e3de06 100644 --- a/test/core/channel/channel_trace_test.cc +++ b/test/core/channel/channel_trace_test.cc @@ -88,7 +88,7 @@ void AddSimpleTrace(ChannelTrace* tracer) { void ValidateChannelTrace(ChannelTrace* tracer, size_t expected_num_event_logged, size_t max_nodes) { if (!max_nodes) return; - grpc_json* json = tracer->RenderJSON(); + grpc_json* json = tracer->RenderJson(); EXPECT_NE(json, nullptr); char* json_str = grpc_json_dump_to_string(json, 0); grpc_json_destroy(json); diff --git a/test/core/channel/channelz_test.cc b/test/core/channel/channelz_test.cc index 058eea914c..1ed36acb61 100644 --- a/test/core/channel/channelz_test.cc +++ b/test/core/channel/channelz_test.cc @@ -67,9 +67,39 @@ grpc_json* GetJsonChild(grpc_json* parent, const char* key) { return nullptr; } +void ValidateJsonArraySize(grpc_json* json, const char* key, + size_t expected_size) { + grpc_json* arr = GetJsonChild(json, key); + if (expected_size == 0) { + ASSERT_EQ(arr, nullptr); + return; + } + ASSERT_NE(arr, nullptr); + ASSERT_EQ(arr->type, GRPC_JSON_ARRAY); + size_t count = 0; + for (grpc_json* child = arr->child; child != nullptr; child = child->next) { + ++count; + } + ASSERT_EQ(count, expected_size); +} + +void ValidateGetTopChannels(size_t expected_channels) { + char* json_str = ChannelzRegistry::GetTopChannels(0); + grpc::testing::ValidateGetTopChannelsResponseProtoJsonTranslation(json_str); + grpc_json* parsed_json = grpc_json_parse_string(json_str); + // This check will naturally have to change when we support pagination. + // tracked: https://github.com/grpc/grpc/issues/16019. + ValidateJsonArraySize(parsed_json, "channel", expected_channels); + grpc_json* end = GetJsonChild(parsed_json, "end"); + EXPECT_NE(end, nullptr); + EXPECT_EQ(end->type, GRPC_JSON_TRUE); + grpc_json_destroy(parsed_json); + gpr_free(json_str); +} + class ChannelFixture { public: - ChannelFixture(int max_trace_nodes) { + ChannelFixture(int max_trace_nodes = 0) { grpc_arg client_a[2]; client_a[0].type = GRPC_ARG_INTEGER; client_a[0].key = @@ -99,6 +129,10 @@ struct validate_channel_data_args { void ValidateChildInteger(grpc_json* json, int64_t expect, const char* key) { grpc_json* gotten_json = GetJsonChild(json, key); + if (expect == 0) { + ASSERT_EQ(gotten_json, nullptr); + return; + } ASSERT_NE(gotten_json, nullptr); int64_t gotten_number = (int64_t)strtol(gotten_json->value, nullptr, 0); EXPECT_EQ(gotten_number, expect); @@ -115,7 +149,7 @@ void ValidateCounters(char* json_str, validate_channel_data_args args) { } void ValidateChannel(ChannelNode* channel, validate_channel_data_args args) { - char* json_str = channel->RenderJSON(); + char* json_str = channel->RenderJsonString(); grpc::testing::ValidateChannelProtoJsonTranslation(json_str); ValidateCounters(json_str, args); gpr_free(json_str); @@ -141,9 +175,7 @@ TEST_P(ChannelzChannelTest, BasicChannel) { ChannelFixture channel(GetParam()); ChannelNode* channelz_channel = grpc_channel_get_channelz_node(channel.channel()); - char* json_str = channelz_channel->RenderJSON(); - ValidateCounters(json_str, {0, 0, 0}); - gpr_free(json_str); + ValidateChannel(channelz_channel, {0, 0, 0}); } TEST(ChannelzChannelTest, ChannelzDisabled) { @@ -199,6 +231,45 @@ TEST_P(ChannelzChannelTest, LastCallStartedMillis) { EXPECT_NE(millis1, millis4); } +TEST(ChannelzGetTopChannelsTest, BasicTest) { + grpc_core::ExecCtx exec_ctx; + ChannelFixture channel; + ValidateGetTopChannels(1); +} + +TEST(ChannelzGetTopChannelsTest, NoChannelsTest) { + grpc_core::ExecCtx exec_ctx; + ValidateGetTopChannels(0); +} + +TEST(ChannelzGetTopChannelsTest, ManyChannelsTest) { + grpc_core::ExecCtx exec_ctx; + ChannelFixture channels[10]; + (void)channels; // suppress unused variable error + ValidateGetTopChannels(10); +} + +TEST(ChannelzGetTopChannelsTest, InternalChannelTest) { + grpc_core::ExecCtx exec_ctx; + ChannelFixture channels[10]; + (void)channels; // suppress unused variable error + // create an internal channel + grpc_arg client_a[2]; + client_a[0].type = GRPC_ARG_INTEGER; + client_a[0].key = + const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL); + client_a[0].value.integer = 1; + client_a[1].type = GRPC_ARG_INTEGER; + client_a[1].key = const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ); + client_a[1].value.integer = true; + grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a}; + grpc_channel* internal_channel = + grpc_insecure_channel_create("fake_target", &client_args, nullptr); + // The internal channel should not be returned from the request + ValidateGetTopChannels(10); + grpc_channel_destroy(internal_channel); +} + INSTANTIATE_TEST_CASE_P(ChannelzChannelTestSweep, ChannelzChannelTest, ::testing::Values(0, 1, 2, 6, 10, 15)); diff --git a/test/core/end2end/tests/channelz.cc b/test/core/end2end/tests/channelz.cc index eb052119ca..533703a2be 100644 --- a/test/core/end2end/tests/channelz.cc +++ b/test/core/end2end/tests/channelz.cc @@ -209,27 +209,27 @@ static void test_channelz(grpc_end2end_test_config config) { grpc_channel_get_channelz_node(f.client); GPR_ASSERT(channelz_channel != nullptr); - char* json = channelz_channel->RenderJSON(); + char* json = channelz_channel->RenderJsonString(); GPR_ASSERT(json != nullptr); - GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"0\"")); - GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\"")); - GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"0\"")); + // nothing is present yet + GPR_ASSERT(nullptr == strstr(json, "\"callsStarted\"")); + GPR_ASSERT(nullptr == strstr(json, "\"callsFailed\"")); + GPR_ASSERT(nullptr == strstr(json, "\"callsSucceeded\"")); gpr_free(json); // one successful request run_one_request(config, f, true); - json = channelz_channel->RenderJSON(); + json = channelz_channel->RenderJsonString(); GPR_ASSERT(json != nullptr); GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"1\"")); - GPR_ASSERT(nullptr != strstr(json, "\"callsFailed\":\"0\"")); GPR_ASSERT(nullptr != strstr(json, "\"callsSucceeded\":\"1\"")); gpr_free(json); // one failed request run_one_request(config, f, false); - json = channelz_channel->RenderJSON(); + json = channelz_channel->RenderJsonString(); GPR_ASSERT(json != nullptr); gpr_log(GPR_INFO, "%s", json); GPR_ASSERT(nullptr != strstr(json, "\"callsStarted\":\"2\"")); @@ -264,7 +264,7 @@ static void test_channelz_with_channel_trace(grpc_end2end_test_config config) { grpc_channel_get_channelz_node(f.client); GPR_ASSERT(channelz_channel != nullptr); - char* json = channelz_channel->RenderJSON(); + char* json = channelz_channel->RenderJsonString(); GPR_ASSERT(json != nullptr); gpr_log(GPR_INFO, "%s", json); GPR_ASSERT(nullptr != strstr(json, "\"trace\"")); diff --git a/test/cpp/util/channel_trace_proto_helper.cc b/test/cpp/util/channel_trace_proto_helper.cc index ee310784c2..137f278640 100644 --- a/test/cpp/util/channel_trace_proto_helper.cc +++ b/test/cpp/util/channel_trace_proto_helper.cc @@ -64,13 +64,17 @@ void VaidateProtoJsonTranslation(char* json_c_str) { } // namespace -void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str) { - VaidateProtoJsonTranslation<grpc::channelz::v1::ChannelTrace>( - tracer_json_c_str); +void ValidateChannelTraceProtoJsonTranslation(char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::ChannelTrace>(json_c_str); } -void ValidateChannelProtoJsonTranslation(char* channel_json_c_str) { - VaidateProtoJsonTranslation<grpc::channelz::v1::Channel>(channel_json_c_str); +void ValidateChannelProtoJsonTranslation(char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::Channel>(json_c_str); +} + +void ValidateGetTopChannelsResponseProtoJsonTranslation(char* json_c_str) { + VaidateProtoJsonTranslation<grpc::channelz::v1::GetTopChannelsResponse>( + json_c_str); } } // namespace testing diff --git a/test/cpp/util/channel_trace_proto_helper.h b/test/cpp/util/channel_trace_proto_helper.h index d1a3603372..74c15f04f1 100644 --- a/test/cpp/util/channel_trace_proto_helper.h +++ b/test/cpp/util/channel_trace_proto_helper.h @@ -22,8 +22,9 @@ namespace grpc { namespace testing { -void ValidateChannelTraceProtoJsonTranslation(char* tracer_json_c_str); -void ValidateChannelProtoJsonTranslation(char* channel_json_c_str); +void ValidateChannelTraceProtoJsonTranslation(char* json_c_str); +void ValidateChannelProtoJsonTranslation(char* json_c_str); +void ValidateGetTopChannelsResponseProtoJsonTranslation(char* json_c_str); } // namespace testing } // namespace grpc |