3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <gtest/gtest.h>
24 #include <grpc/support/alloc.h>
25 #include <grpc/support/log.h>
27 #include "src/core/lib/channel/channel_trace.h"
28 #include "src/core/lib/channel/channelz.h"
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"
33 #include "src/core/lib/surface/channel.h"
34 #include "src/core/lib/surface/server.h"
36 #include "test/core/util/test_config.h"
37 #include "test/cpp/util/channel_trace_proto_helper.h"
39 #include <grpc/support/string_util.h>
47 // testing peer to access channel internals
48 class CallCountingHelperPeer {
50 explicit CallCountingHelperPeer(CallCountingHelper* node) : node_(node) {}
51 grpc_millis last_call_started_millis() const {
52 CallCountingHelper::CounterData data;
53 node_->CollectData(&data);
54 return (grpc_millis)gpr_atm_no_barrier_load(&data.last_call_started_millis);
58 CallCountingHelper* node_;
63 grpc_json* GetJsonChild(grpc_json* parent, const char* key) {
64 EXPECT_NE(parent, nullptr);
65 for (grpc_json* child = parent->child; child != nullptr;
66 child = child->next) {
67 if (child->key != nullptr && strcmp(child->key, key) == 0) return child;
72 void ValidateJsonArraySize(grpc_json* json, const char* key,
73 size_t expected_size) {
74 grpc_json* arr = GetJsonChild(json, key);
75 if (expected_size == 0) {
76 ASSERT_EQ(arr, nullptr);
79 ASSERT_NE(arr, nullptr);
80 ASSERT_EQ(arr->type, GRPC_JSON_ARRAY);
82 for (grpc_json* child = arr->child; child != nullptr; child = child->next) {
85 EXPECT_EQ(count, expected_size);
88 std::vector<intptr_t> GetUuidListFromArray(grpc_json* arr) {
89 EXPECT_EQ(arr->type, GRPC_JSON_ARRAY);
90 std::vector<intptr_t> uuids;
91 for (grpc_json* child = arr->child; child != nullptr; child = child->next) {
92 grpc_json* it = GetJsonChild(child, "ref");
93 EXPECT_NE(it, nullptr);
94 it = GetJsonChild(it, "channelId");
95 EXPECT_NE(it, nullptr);
96 uuids.push_back(atoi(it->value));
101 void ValidateGetTopChannels(size_t expected_channels) {
102 char* json_str = ChannelzRegistry::GetTopChannels(0);
103 grpc::testing::ValidateGetTopChannelsResponseProtoJsonTranslation(json_str);
104 grpc_json* parsed_json = grpc_json_parse_string(json_str);
105 // This check will naturally have to change when we support pagination.
106 // tracked: https://github.com/grpc/grpc/issues/16019.
107 ValidateJsonArraySize(parsed_json, "channel", expected_channels);
108 grpc_json* end = GetJsonChild(parsed_json, "end");
109 ASSERT_NE(end, nullptr);
110 EXPECT_EQ(end->type, GRPC_JSON_TRUE);
111 grpc_json_destroy(parsed_json);
113 // also check that the core API formats this correctly
114 char* core_api_json_str = grpc_channelz_get_top_channels(0);
115 grpc::testing::ValidateGetTopChannelsResponseProtoJsonTranslation(
117 gpr_free(core_api_json_str);
120 void ValidateGetServers(size_t expected_servers) {
121 char* json_str = ChannelzRegistry::GetServers(0);
122 grpc::testing::ValidateGetServersResponseProtoJsonTranslation(json_str);
123 grpc_json* parsed_json = grpc_json_parse_string(json_str);
124 // This check will naturally have to change when we support pagination.
125 // tracked: https://github.com/grpc/grpc/issues/16019.
126 ValidateJsonArraySize(parsed_json, "server", expected_servers);
127 grpc_json* end = GetJsonChild(parsed_json, "end");
128 ASSERT_NE(end, nullptr);
129 EXPECT_EQ(end->type, GRPC_JSON_TRUE);
130 grpc_json_destroy(parsed_json);
132 // also check that the core API formats this correctly
133 char* core_api_json_str = grpc_channelz_get_servers(0);
134 grpc::testing::ValidateGetServersResponseProtoJsonTranslation(
136 gpr_free(core_api_json_str);
139 class ChannelFixture {
141 ChannelFixture(int max_tracer_event_memory = 0) {
142 grpc_arg client_a[] = {
143 grpc_channel_arg_integer_create(
144 const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE),
145 max_tracer_event_memory),
146 grpc_channel_arg_integer_create(
147 const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), true)};
148 grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a};
150 grpc_insecure_channel_create("fake_target", &client_args, nullptr);
153 ~ChannelFixture() { grpc_channel_destroy(channel_); }
155 grpc_channel* channel() { return channel_; }
158 grpc_channel* channel_;
161 class ServerFixture {
163 explicit ServerFixture(int max_tracer_event_memory = 0) {
164 grpc_arg server_a[] = {
165 grpc_channel_arg_integer_create(
166 const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE),
167 max_tracer_event_memory),
168 grpc_channel_arg_integer_create(
169 const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), true),
171 grpc_channel_args server_args = {GPR_ARRAY_SIZE(server_a), server_a};
172 server_ = grpc_server_create(&server_args, nullptr);
175 ~ServerFixture() { grpc_server_destroy(server_); }
177 grpc_server* server() const { return server_; }
180 grpc_server* server_;
183 struct validate_channel_data_args {
184 int64_t calls_started;
185 int64_t calls_failed;
186 int64_t calls_succeeded;
189 void ValidateChildInteger(grpc_json* json, int64_t expect, const char* key) {
190 grpc_json* gotten_json = GetJsonChild(json, key);
192 ASSERT_EQ(gotten_json, nullptr);
195 ASSERT_NE(gotten_json, nullptr);
196 int64_t gotten_number = (int64_t)strtol(gotten_json->value, nullptr, 0);
197 EXPECT_EQ(gotten_number, expect);
200 void ValidateCounters(char* json_str, validate_channel_data_args args) {
201 grpc_json* json = grpc_json_parse_string(json_str);
202 ASSERT_NE(json, nullptr);
203 grpc_json* data = GetJsonChild(json, "data");
204 ValidateChildInteger(data, args.calls_started, "callsStarted");
205 ValidateChildInteger(data, args.calls_failed, "callsFailed");
206 ValidateChildInteger(data, args.calls_succeeded, "callsSucceeded");
207 grpc_json_destroy(json);
210 void ValidateChannel(ChannelNode* channel, validate_channel_data_args args) {
211 char* json_str = channel->RenderJsonString();
212 grpc::testing::ValidateChannelProtoJsonTranslation(json_str);
213 ValidateCounters(json_str, args);
215 // also check that the core API formats this the correct way
216 char* core_api_json_str = grpc_channelz_get_channel(channel->uuid());
217 grpc::testing::ValidateGetChannelResponseProtoJsonTranslation(
219 gpr_free(core_api_json_str);
222 void ValidateServer(ServerNode* server, validate_channel_data_args args) {
223 char* json_str = server->RenderJsonString();
224 grpc::testing::ValidateServerProtoJsonTranslation(json_str);
225 ValidateCounters(json_str, args);
227 // also check that the core API formats this the correct way
228 char* core_api_json_str = grpc_channelz_get_server(server->uuid());
229 grpc::testing::ValidateGetServerResponseProtoJsonTranslation(
231 gpr_free(core_api_json_str);
234 grpc_millis GetLastCallStartedMillis(CallCountingHelper* channel) {
235 CallCountingHelperPeer peer(channel);
236 return peer.last_call_started_millis();
239 void ChannelzSleep(int64_t sleep_us) {
240 gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
241 gpr_time_from_micros(sleep_us, GPR_TIMESPAN)));
242 grpc_core::ExecCtx::Get()->InvalidateNow();
245 } // anonymous namespace
247 class ChannelzChannelTest : public ::testing::TestWithParam<size_t> {};
249 TEST_P(ChannelzChannelTest, BasicChannel) {
250 grpc_core::ExecCtx exec_ctx;
251 ChannelFixture channel(GetParam());
252 ChannelNode* channelz_channel =
253 grpc_channel_get_channelz_node(channel.channel());
254 ValidateChannel(channelz_channel, {0, 0, 0});
257 TEST(ChannelzChannelTest, ChannelzDisabled) {
258 grpc_core::ExecCtx exec_ctx;
259 // explicitly disable channelz
261 grpc_channel_arg_integer_create(
262 const_cast<char*>(GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE),
264 grpc_channel_arg_integer_create(
265 const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), false)};
266 grpc_channel_args args = {GPR_ARRAY_SIZE(arg), arg};
267 grpc_channel* channel =
268 grpc_insecure_channel_create("fake_target", &args, nullptr);
269 ChannelNode* channelz_channel = grpc_channel_get_channelz_node(channel);
270 ASSERT_EQ(channelz_channel, nullptr);
271 grpc_channel_destroy(channel);
274 TEST_P(ChannelzChannelTest, BasicChannelAPIFunctionality) {
275 grpc_core::ExecCtx exec_ctx;
276 ChannelFixture channel(GetParam());
277 ChannelNode* channelz_channel =
278 grpc_channel_get_channelz_node(channel.channel());
279 channelz_channel->RecordCallStarted();
280 channelz_channel->RecordCallFailed();
281 channelz_channel->RecordCallSucceeded();
282 ValidateChannel(channelz_channel, {1, 1, 1});
283 channelz_channel->RecordCallStarted();
284 channelz_channel->RecordCallFailed();
285 channelz_channel->RecordCallSucceeded();
286 channelz_channel->RecordCallStarted();
287 channelz_channel->RecordCallFailed();
288 channelz_channel->RecordCallSucceeded();
289 ValidateChannel(channelz_channel, {3, 3, 3});
292 TEST_P(ChannelzChannelTest, LastCallStartedMillis) {
293 grpc_core::ExecCtx exec_ctx;
294 CallCountingHelper counter;
295 // start a call to set the last call started timestamp
296 counter.RecordCallStarted();
297 grpc_millis millis1 = GetLastCallStartedMillis(&counter);
298 // time gone by should not affect the timestamp
300 grpc_millis millis2 = GetLastCallStartedMillis(&counter);
301 EXPECT_EQ(millis1, millis2);
302 // calls succeeded or failed should not affect the timestamp
304 counter.RecordCallFailed();
305 counter.RecordCallSucceeded();
306 grpc_millis millis3 = GetLastCallStartedMillis(&counter);
307 EXPECT_EQ(millis1, millis3);
308 // another call started should affect the timestamp
309 // sleep for extra long to avoid flakes (since we cache Now())
311 counter.RecordCallStarted();
312 grpc_millis millis4 = GetLastCallStartedMillis(&counter);
313 EXPECT_NE(millis1, millis4);
316 class ChannelzRegistryBasedTest : public ::testing::TestWithParam<size_t> {
318 // ensure we always have a fresh registry for tests.
319 void SetUp() override {
320 ChannelzRegistry::Shutdown();
321 ChannelzRegistry::Init();
324 void TearDown() override {
325 ChannelzRegistry::Shutdown();
326 ChannelzRegistry::Init();
330 TEST_F(ChannelzRegistryBasedTest, BasicGetTopChannelsTest) {
331 grpc_core::ExecCtx exec_ctx;
332 ChannelFixture channel;
333 ValidateGetTopChannels(1);
336 TEST_F(ChannelzRegistryBasedTest, NoChannelsTest) {
337 grpc_core::ExecCtx exec_ctx;
338 ValidateGetTopChannels(0);
341 TEST_F(ChannelzRegistryBasedTest, ManyChannelsTest) {
342 grpc_core::ExecCtx exec_ctx;
343 ChannelFixture channels[10];
344 (void)channels; // suppress unused variable error
345 ValidateGetTopChannels(10);
348 TEST_F(ChannelzRegistryBasedTest, GetTopChannelsPagination) {
349 grpc_core::ExecCtx exec_ctx;
350 // this is over the pagination limit.
351 ChannelFixture channels[150];
352 (void)channels; // suppress unused variable error
353 char* json_str = ChannelzRegistry::GetTopChannels(0);
354 grpc::testing::ValidateGetTopChannelsResponseProtoJsonTranslation(json_str);
355 grpc_json* parsed_json = grpc_json_parse_string(json_str);
356 // 100 is the pagination limit.
357 ValidateJsonArraySize(parsed_json, "channel", 100);
358 grpc_json* end = GetJsonChild(parsed_json, "end");
359 EXPECT_EQ(end, nullptr);
360 grpc_json_destroy(parsed_json);
362 // Now we get the rest
363 json_str = ChannelzRegistry::GetTopChannels(101);
364 grpc::testing::ValidateGetTopChannelsResponseProtoJsonTranslation(json_str);
365 parsed_json = grpc_json_parse_string(json_str);
366 ValidateJsonArraySize(parsed_json, "channel", 50);
367 end = GetJsonChild(parsed_json, "end");
368 ASSERT_NE(end, nullptr);
369 EXPECT_EQ(end->type, GRPC_JSON_TRUE);
370 grpc_json_destroy(parsed_json);
374 TEST_F(ChannelzRegistryBasedTest, GetTopChannelsUuidCheck) {
375 const intptr_t kNumChannels = 50;
376 grpc_core::ExecCtx exec_ctx;
377 ChannelFixture channels[kNumChannels];
378 (void)channels; // suppress unused variable error
379 char* json_str = ChannelzRegistry::GetTopChannels(0);
380 grpc_json* parsed_json = grpc_json_parse_string(json_str);
381 ValidateJsonArraySize(parsed_json, "channel", kNumChannels);
382 grpc_json* json_channels = GetJsonChild(parsed_json, "channel");
383 std::vector<intptr_t> uuids = GetUuidListFromArray(json_channels);
384 for (int i = 0; i < kNumChannels; ++i) {
385 EXPECT_EQ(i + 1, uuids[i]);
387 grpc_json_destroy(parsed_json);
391 TEST_F(ChannelzRegistryBasedTest, GetTopChannelsMiddleUuidCheck) {
392 const intptr_t kNumChannels = 50;
393 const intptr_t kMidQuery = 40;
394 grpc_core::ExecCtx exec_ctx;
395 ChannelFixture channels[kNumChannels];
396 (void)channels; // suppress unused variable error
397 // only query for the end of the channels
398 char* json_str = ChannelzRegistry::GetTopChannels(kMidQuery);
399 grpc_json* parsed_json = grpc_json_parse_string(json_str);
400 ValidateJsonArraySize(parsed_json, "channel", kNumChannels - kMidQuery + 1);
401 grpc_json* json_channels = GetJsonChild(parsed_json, "channel");
402 std::vector<intptr_t> uuids = GetUuidListFromArray(json_channels);
403 for (size_t i = 0; i < uuids.size(); ++i) {
404 EXPECT_EQ(static_cast<intptr_t>(kMidQuery + i), uuids[i]);
406 grpc_json_destroy(parsed_json);
410 TEST_F(ChannelzRegistryBasedTest, GetTopChannelsNoHitUuid) {
411 grpc_core::ExecCtx exec_ctx;
412 ChannelFixture pre_channels[40]; // will take uuid[1, 40]
413 (void)pre_channels; // suppress unused variable error
414 ServerFixture servers[10]; // will take uuid[41, 50]
415 (void)servers; // suppress unused variable error
416 ChannelFixture channels[10]; // will take uuid[51, 60]
417 (void)channels; // suppress unused variable error
418 // query in the middle of the server channels
419 char* json_str = ChannelzRegistry::GetTopChannels(45);
420 grpc_json* parsed_json = grpc_json_parse_string(json_str);
421 ValidateJsonArraySize(parsed_json, "channel", 10);
422 grpc_json* json_channels = GetJsonChild(parsed_json, "channel");
423 std::vector<intptr_t> uuids = GetUuidListFromArray(json_channels);
424 for (size_t i = 0; i < uuids.size(); ++i) {
425 EXPECT_EQ(static_cast<intptr_t>(51 + i), uuids[i]);
427 grpc_json_destroy(parsed_json);
431 TEST_F(ChannelzRegistryBasedTest, GetTopChannelsMoreGaps) {
432 grpc_core::ExecCtx exec_ctx;
433 ChannelFixture channel_with_uuid1;
434 { ServerFixture channel_with_uuid2; }
435 ChannelFixture channel_with_uuid3;
436 { ServerFixture server_with_uuid4; }
437 ChannelFixture channel_with_uuid5;
438 // Current state of list: [1, NULL, 3, NULL, 5]
439 char* json_str = ChannelzRegistry::GetTopChannels(2);
440 grpc_json* parsed_json = grpc_json_parse_string(json_str);
441 ValidateJsonArraySize(parsed_json, "channel", 2);
442 grpc_json* json_channels = GetJsonChild(parsed_json, "channel");
443 std::vector<intptr_t> uuids = GetUuidListFromArray(json_channels);
444 EXPECT_EQ(static_cast<intptr_t>(3), uuids[0]);
445 EXPECT_EQ(static_cast<intptr_t>(5), uuids[1]);
446 grpc_json_destroy(parsed_json);
448 json_str = ChannelzRegistry::GetTopChannels(4);
449 parsed_json = grpc_json_parse_string(json_str);
450 ValidateJsonArraySize(parsed_json, "channel", 1);
451 json_channels = GetJsonChild(parsed_json, "channel");
452 uuids = GetUuidListFromArray(json_channels);
453 EXPECT_EQ(static_cast<intptr_t>(5), uuids[0]);
454 grpc_json_destroy(parsed_json);
458 TEST_F(ChannelzRegistryBasedTest, GetTopChannelsUuidAfterCompaction) {
459 const intptr_t kLoopIterations = 50;
460 grpc_core::ExecCtx exec_ctx;
461 std::vector<UniquePtr<ChannelFixture>> even_channels;
463 // these will delete and unregister themselves after this block.
464 std::vector<UniquePtr<ChannelFixture>> odd_channels;
465 for (int i = 0; i < kLoopIterations; i++) {
466 odd_channels.push_back(MakeUnique<ChannelFixture>());
467 even_channels.push_back(MakeUnique<ChannelFixture>());
470 char* json_str = ChannelzRegistry::GetTopChannels(0);
471 grpc_json* parsed_json = grpc_json_parse_string(json_str);
472 ValidateJsonArraySize(parsed_json, "channel", kLoopIterations);
473 grpc_json* json_channels = GetJsonChild(parsed_json, "channel");
474 std::vector<intptr_t> uuids = GetUuidListFromArray(json_channels);
475 for (int i = 0; i < kLoopIterations; ++i) {
476 // only the even uuids will still be present.
477 EXPECT_EQ((i + 1) * 2, uuids[i]);
479 grpc_json_destroy(parsed_json);
483 TEST_F(ChannelzRegistryBasedTest, InternalChannelTest) {
484 grpc_core::ExecCtx exec_ctx;
485 ChannelFixture channels[10];
486 (void)channels; // suppress unused variable error
487 // create an internal channel
488 grpc_arg client_a[2];
489 client_a[0] = grpc_channel_arg_integer_create(
490 const_cast<char*>(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), true);
491 client_a[1] = grpc_channel_arg_integer_create(
492 const_cast<char*>(GRPC_ARG_ENABLE_CHANNELZ), true);
493 grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a};
494 grpc_channel* internal_channel =
495 grpc_insecure_channel_create("fake_target", &client_args, nullptr);
496 // The internal channel should not be returned from the request
497 ValidateGetTopChannels(10);
498 grpc_channel_destroy(internal_channel);
501 TEST(ChannelzServerTest, BasicServerAPIFunctionality) {
502 grpc_core::ExecCtx exec_ctx;
503 ServerFixture server(10);
504 ServerNode* channelz_server = grpc_server_get_channelz_node(server.server());
505 channelz_server->RecordCallStarted();
506 channelz_server->RecordCallFailed();
507 channelz_server->RecordCallSucceeded();
508 ValidateServer(channelz_server, {1, 1, 1});
509 channelz_server->RecordCallStarted();
510 channelz_server->RecordCallFailed();
511 channelz_server->RecordCallSucceeded();
512 channelz_server->RecordCallStarted();
513 channelz_server->RecordCallFailed();
514 channelz_server->RecordCallSucceeded();
515 ValidateServer(channelz_server, {3, 3, 3});
518 TEST_F(ChannelzRegistryBasedTest, BasicGetServersTest) {
519 grpc_core::ExecCtx exec_ctx;
520 ServerFixture server;
521 ValidateGetServers(1);
524 TEST_F(ChannelzRegistryBasedTest, NoServersTest) {
525 grpc_core::ExecCtx exec_ctx;
526 ValidateGetServers(0);
529 TEST_F(ChannelzRegistryBasedTest, ManyServersTest) {
530 grpc_core::ExecCtx exec_ctx;
531 ServerFixture servers[10];
532 (void)servers; // suppress unused variable error
533 ValidateGetServers(10);
536 INSTANTIATE_TEST_CASE_P(ChannelzChannelTestSweep, ChannelzChannelTest,
537 ::testing::Values(0, 8, 64, 1024, 1024 * 1024));
539 } // namespace testing
540 } // namespace channelz
541 } // namespace grpc_core
543 int main(int argc, char** argv) {
544 grpc::testing::TestEnvironment env(argc, argv);
546 ::testing::InitGoogleTest(&argc, argv);
547 int ret = RUN_ALL_TESTS();