ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 1 | /* |
| 2 | * |
| 3 | * Copyright 2017 gRPC authors. |
| 4 | * |
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at |
| 8 | * |
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | * |
| 17 | */ |
| 18 | |
| 19 | #include <stdlib.h> |
| 20 | #include <string.h> |
| 21 | |
| 22 | #include <gtest/gtest.h> |
| 23 | |
| 24 | #include <grpc/support/alloc.h> |
| 25 | #include <grpc/support/log.h> |
| 26 | |
| 27 | #include "src/core/lib/channel/channel_trace.h" |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 28 | #include "src/core/lib/channel/channelz.h" |
ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 29 | #include "src/core/lib/channel/channelz_registry.h" |
| 30 | #include "src/core/lib/gpr/useful.h" |
| 31 | #include "src/core/lib/iomgr/exec_ctx.h" |
| 32 | #include "src/core/lib/json/json.h" |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 33 | #include "src/core/lib/surface/channel.h" |
ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 34 | |
| 35 | #include "test/core/util/test_config.h" |
| 36 | #include "test/cpp/util/channel_trace_proto_helper.h" |
| 37 | |
ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 38 | #include <grpc/support/string_util.h> |
| 39 | #include <stdlib.h> |
| 40 | #include <string.h> |
| 41 | |
| 42 | namespace grpc_core { |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 43 | namespace channelz { |
ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 44 | namespace testing { |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 45 | |
| 46 | // testing peer to access channel internals |
ncteisen | 6c987cb | 2018-06-08 09:30:12 -0700 | [diff] [blame] | 47 | class ChannelNodePeer { |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 48 | public: |
ncteisen | 6c987cb | 2018-06-08 09:30:12 -0700 | [diff] [blame] | 49 | ChannelNodePeer(ChannelNode* channel) : channel_(channel) {} |
Noah Eisen | ea424b8 | 2018-06-07 13:02:48 -0700 | [diff] [blame] | 50 | grpc_millis last_call_started_millis() { |
| 51 | return (grpc_millis)gpr_atm_no_barrier_load( |
| 52 | &channel_->last_call_started_millis_); |
| 53 | } |
| 54 | |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 55 | private: |
ncteisen | 6c987cb | 2018-06-08 09:30:12 -0700 | [diff] [blame] | 56 | ChannelNode* channel_; |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 57 | }; |
| 58 | |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 59 | namespace { |
ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 60 | |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 61 | grpc_json* GetJsonChild(grpc_json* parent, const char* key) { |
| 62 | EXPECT_NE(parent, nullptr); |
| 63 | for (grpc_json* child = parent->child; child != nullptr; |
| 64 | child = child->next) { |
| 65 | if (child->key != nullptr && strcmp(child->key, key) == 0) return child; |
| 66 | } |
| 67 | return nullptr; |
| 68 | } |
| 69 | |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 70 | void ValidateJsonArraySize(grpc_json* json, const char* key, |
| 71 | size_t expected_size) { |
| 72 | grpc_json* arr = GetJsonChild(json, key); |
| 73 | if (expected_size == 0) { |
| 74 | ASSERT_EQ(arr, nullptr); |
| 75 | return; |
| 76 | } |
| 77 | ASSERT_NE(arr, nullptr); |
| 78 | ASSERT_EQ(arr->type, GRPC_JSON_ARRAY); |
| 79 | size_t count = 0; |
| 80 | for (grpc_json* child = arr->child; child != nullptr; child = child->next) { |
| 81 | ++count; |
| 82 | } |
ncteisen | 5d373c4 | 2018-07-17 11:57:31 -0700 | [diff] [blame] | 83 | EXPECT_EQ(count, expected_size); |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | void ValidateGetTopChannels(size_t expected_channels) { |
| 87 | char* json_str = ChannelzRegistry::GetTopChannels(0); |
| 88 | grpc::testing::ValidateGetTopChannelsResponseProtoJsonTranslation(json_str); |
| 89 | grpc_json* parsed_json = grpc_json_parse_string(json_str); |
| 90 | // This check will naturally have to change when we support pagination. |
| 91 | // tracked: https://github.com/grpc/grpc/issues/16019. |
| 92 | ValidateJsonArraySize(parsed_json, "channel", expected_channels); |
| 93 | grpc_json* end = GetJsonChild(parsed_json, "end"); |
ncteisen | 5d373c4 | 2018-07-17 11:57:31 -0700 | [diff] [blame] | 94 | ASSERT_NE(end, nullptr); |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 95 | EXPECT_EQ(end->type, GRPC_JSON_TRUE); |
| 96 | grpc_json_destroy(parsed_json); |
| 97 | gpr_free(json_str); |
| 98 | } |
| 99 | |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 100 | class ChannelFixture { |
| 101 | public: |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 102 | ChannelFixture(int max_trace_nodes = 0) { |
ncteisen | c7166ae | 2018-06-15 11:04:37 -0400 | [diff] [blame] | 103 | grpc_arg client_a[2]; |
ncteisen | 5d373c4 | 2018-07-17 11:57:31 -0700 | [diff] [blame] | 104 | client_a[0] = grpc_channel_arg_integer_create( |
| 105 | const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENTS_PER_NODE), |
| 106 | max_trace_nodes); |
| 107 | client_a[1] = grpc_channel_arg_integer_create( |
| 108 | const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), true); |
ncteisen | c7166ae | 2018-06-15 11:04:37 -0400 | [diff] [blame] | 109 | grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a}; |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 110 | channel_ = |
| 111 | grpc_insecure_channel_create("fake_target", &client_args, nullptr); |
| 112 | } |
| 113 | |
| 114 | ~ChannelFixture() { grpc_channel_destroy(channel_); } |
| 115 | |
| 116 | grpc_channel* channel() { return channel_; } |
| 117 | |
| 118 | private: |
| 119 | grpc_channel* channel_; |
| 120 | }; |
| 121 | |
| 122 | struct validate_channel_data_args { |
| 123 | int64_t calls_started; |
| 124 | int64_t calls_failed; |
| 125 | int64_t calls_succeeded; |
| 126 | }; |
| 127 | |
| 128 | void ValidateChildInteger(grpc_json* json, int64_t expect, const char* key) { |
| 129 | grpc_json* gotten_json = GetJsonChild(json, key); |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 130 | if (expect == 0) { |
| 131 | ASSERT_EQ(gotten_json, nullptr); |
| 132 | return; |
| 133 | } |
ncteisen | c7166ae | 2018-06-15 11:04:37 -0400 | [diff] [blame] | 134 | ASSERT_NE(gotten_json, nullptr); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 135 | int64_t gotten_number = (int64_t)strtol(gotten_json->value, nullptr, 0); |
| 136 | EXPECT_EQ(gotten_number, expect); |
| 137 | } |
| 138 | |
ncteisen | 9b488f7 | 2018-06-07 18:38:28 -0700 | [diff] [blame] | 139 | void ValidateCounters(char* json_str, validate_channel_data_args args) { |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 140 | grpc_json* json = grpc_json_parse_string(json_str); |
ncteisen | c7166ae | 2018-06-15 11:04:37 -0400 | [diff] [blame] | 141 | ASSERT_NE(json, nullptr); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 142 | grpc_json* data = GetJsonChild(json, "data"); |
| 143 | ValidateChildInteger(data, args.calls_started, "callsStarted"); |
| 144 | ValidateChildInteger(data, args.calls_failed, "callsFailed"); |
| 145 | ValidateChildInteger(data, args.calls_succeeded, "callsSucceeded"); |
| 146 | grpc_json_destroy(json); |
ncteisen | 9b488f7 | 2018-06-07 18:38:28 -0700 | [diff] [blame] | 147 | } |
| 148 | |
ncteisen | 6c987cb | 2018-06-08 09:30:12 -0700 | [diff] [blame] | 149 | void ValidateChannel(ChannelNode* channel, validate_channel_data_args args) { |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 150 | char* json_str = channel->RenderJsonString(); |
ncteisen | 9b488f7 | 2018-06-07 18:38:28 -0700 | [diff] [blame] | 151 | grpc::testing::ValidateChannelProtoJsonTranslation(json_str); |
| 152 | ValidateCounters(json_str, args); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 153 | gpr_free(json_str); |
| 154 | } |
| 155 | |
ncteisen | 6c987cb | 2018-06-08 09:30:12 -0700 | [diff] [blame] | 156 | grpc_millis GetLastCallStartedMillis(ChannelNode* channel) { |
| 157 | ChannelNodePeer peer(channel); |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 158 | return peer.last_call_started_millis(); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | void ChannelzSleep(int64_t sleep_us) { |
| 162 | gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), |
| 163 | gpr_time_from_micros(sleep_us, GPR_TIMESPAN))); |
| 164 | grpc_core::ExecCtx::Get()->InvalidateNow(); |
| 165 | } |
| 166 | |
| 167 | } // anonymous namespace |
| 168 | |
| 169 | class ChannelzChannelTest : public ::testing::TestWithParam<size_t> {}; |
| 170 | |
| 171 | TEST_P(ChannelzChannelTest, BasicChannel) { |
| 172 | grpc_core::ExecCtx exec_ctx; |
| 173 | ChannelFixture channel(GetParam()); |
ncteisen | 16280c7 | 2018-06-08 09:44:25 -0700 | [diff] [blame] | 174 | ChannelNode* channelz_channel = |
| 175 | grpc_channel_get_channelz_node(channel.channel()); |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 176 | ValidateChannel(channelz_channel, {0, 0, 0}); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 177 | } |
| 178 | |
ncteisen | c7166ae | 2018-06-15 11:04:37 -0400 | [diff] [blame] | 179 | TEST(ChannelzChannelTest, ChannelzDisabled) { |
| 180 | grpc_core::ExecCtx exec_ctx; |
| 181 | grpc_channel* channel = |
| 182 | grpc_insecure_channel_create("fake_target", nullptr, nullptr); |
| 183 | ChannelNode* channelz_channel = grpc_channel_get_channelz_node(channel); |
ncteisen | 1ab1287 | 2018-06-19 17:09:53 -0700 | [diff] [blame] | 184 | ASSERT_EQ(channelz_channel, nullptr); |
ncteisen | 4d1da60 | 2018-06-15 14:54:26 -0400 | [diff] [blame] | 185 | grpc_channel_destroy(channel); |
ncteisen | c7166ae | 2018-06-15 11:04:37 -0400 | [diff] [blame] | 186 | } |
| 187 | |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 188 | TEST_P(ChannelzChannelTest, BasicChannelAPIFunctionality) { |
| 189 | grpc_core::ExecCtx exec_ctx; |
| 190 | ChannelFixture channel(GetParam()); |
ncteisen | 16280c7 | 2018-06-08 09:44:25 -0700 | [diff] [blame] | 191 | ChannelNode* channelz_channel = |
| 192 | grpc_channel_get_channelz_node(channel.channel()); |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 193 | channelz_channel->RecordCallStarted(); |
| 194 | channelz_channel->RecordCallFailed(); |
| 195 | channelz_channel->RecordCallSucceeded(); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 196 | ValidateChannel(channelz_channel, {1, 1, 1}); |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 197 | channelz_channel->RecordCallStarted(); |
| 198 | channelz_channel->RecordCallFailed(); |
| 199 | channelz_channel->RecordCallSucceeded(); |
| 200 | channelz_channel->RecordCallStarted(); |
| 201 | channelz_channel->RecordCallFailed(); |
| 202 | channelz_channel->RecordCallSucceeded(); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 203 | ValidateChannel(channelz_channel, {3, 3, 3}); |
| 204 | } |
| 205 | |
ncteisen | d23739e | 2018-06-07 12:55:15 -0700 | [diff] [blame] | 206 | TEST_P(ChannelzChannelTest, LastCallStartedMillis) { |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 207 | grpc_core::ExecCtx exec_ctx; |
| 208 | ChannelFixture channel(GetParam()); |
ncteisen | 16280c7 | 2018-06-08 09:44:25 -0700 | [diff] [blame] | 209 | ChannelNode* channelz_channel = |
| 210 | grpc_channel_get_channelz_node(channel.channel()); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 211 | // start a call to set the last call started timestamp |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 212 | channelz_channel->RecordCallStarted(); |
| 213 | grpc_millis millis1 = GetLastCallStartedMillis(channelz_channel); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 214 | // time gone by should not affect the timestamp |
| 215 | ChannelzSleep(100); |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 216 | grpc_millis millis2 = GetLastCallStartedMillis(channelz_channel); |
| 217 | EXPECT_EQ(millis1, millis2); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 218 | // calls succeeded or failed should not affect the timestamp |
| 219 | ChannelzSleep(100); |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 220 | channelz_channel->RecordCallFailed(); |
| 221 | channelz_channel->RecordCallSucceeded(); |
| 222 | grpc_millis millis3 = GetLastCallStartedMillis(channelz_channel); |
| 223 | EXPECT_EQ(millis1, millis3); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 224 | // another call started should affect the timestamp |
| 225 | // sleep for extra long to avoid flakes (since we cache Now()) |
| 226 | ChannelzSleep(5000); |
ncteisen | c845ba6 | 2018-06-07 10:14:55 -0700 | [diff] [blame] | 227 | channelz_channel->RecordCallStarted(); |
| 228 | grpc_millis millis4 = GetLastCallStartedMillis(channelz_channel); |
| 229 | EXPECT_NE(millis1, millis4); |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 230 | } |
| 231 | |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 232 | TEST(ChannelzGetTopChannelsTest, BasicTest) { |
| 233 | grpc_core::ExecCtx exec_ctx; |
| 234 | ChannelFixture channel; |
| 235 | ValidateGetTopChannels(1); |
| 236 | } |
| 237 | |
| 238 | TEST(ChannelzGetTopChannelsTest, NoChannelsTest) { |
| 239 | grpc_core::ExecCtx exec_ctx; |
| 240 | ValidateGetTopChannels(0); |
| 241 | } |
| 242 | |
| 243 | TEST(ChannelzGetTopChannelsTest, ManyChannelsTest) { |
| 244 | grpc_core::ExecCtx exec_ctx; |
| 245 | ChannelFixture channels[10]; |
| 246 | (void)channels; // suppress unused variable error |
| 247 | ValidateGetTopChannels(10); |
| 248 | } |
| 249 | |
| 250 | TEST(ChannelzGetTopChannelsTest, InternalChannelTest) { |
| 251 | grpc_core::ExecCtx exec_ctx; |
| 252 | ChannelFixture channels[10]; |
| 253 | (void)channels; // suppress unused variable error |
| 254 | // create an internal channel |
| 255 | grpc_arg client_a[2]; |
ncteisen | 5d373c4 | 2018-07-17 11:57:31 -0700 | [diff] [blame] | 256 | client_a[0] = grpc_channel_arg_integer_create( |
| 257 | const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), true); |
| 258 | client_a[1] = grpc_channel_arg_integer_create( |
| 259 | const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), true); |
ncteisen | 97066fd | 2018-07-13 14:05:27 -0700 | [diff] [blame] | 260 | grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a}; |
| 261 | grpc_channel* internal_channel = |
| 262 | grpc_insecure_channel_create("fake_target", &client_args, nullptr); |
| 263 | // The internal channel should not be returned from the request |
| 264 | ValidateGetTopChannels(10); |
| 265 | grpc_channel_destroy(internal_channel); |
| 266 | } |
| 267 | |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 268 | INSTANTIATE_TEST_CASE_P(ChannelzChannelTestSweep, ChannelzChannelTest, |
| 269 | ::testing::Values(0, 1, 2, 6, 10, 15)); |
ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 270 | |
| 271 | } // namespace testing |
ncteisen | 9a1bb05 | 2018-05-30 16:17:06 -0700 | [diff] [blame] | 272 | } // namespace channelz |
ncteisen | c3c6e06 | 2018-05-09 11:10:21 -0700 | [diff] [blame] | 273 | } // namespace grpc_core |
| 274 | |
| 275 | int main(int argc, char** argv) { |
| 276 | grpc_test_init(argc, argv); |
| 277 | grpc_init(); |
| 278 | ::testing::InitGoogleTest(&argc, argv); |
| 279 | int ret = RUN_ALL_TESTS(); |
| 280 | grpc_shutdown(); |
| 281 | return ret; |
| 282 | } |