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 "src/core/channelz/channel_trace.h"
20
21 #include <grpc/credentials.h>
22 #include <grpc/grpc.h>
23 #include <grpc/grpc_security.h>
24 #include <grpc/impl/channel_arg_names.h>
25 #include <stdlib.h>
26
27 #include <list>
28 #include <string>
29 #include <thread>
30
31 #include "absl/synchronization/notification.h"
32 #include "gmock/gmock.h"
33 #include "gtest/gtest.h"
34 #include "src/core/channelz/channelz.h"
35 #include "src/core/lib/channel/channel_args.h"
36 #include "src/core/lib/iomgr/exec_ctx.h"
37 #include "src/core/util/json/json.h"
38 #include "src/core/util/json/json_writer.h"
39 #include "test/core/test_util/test_config.h"
40 #include "test/cpp/util/channel_trace_proto_helper.h"
41
42 namespace grpc_core {
43 namespace channelz {
44 namespace testing {
45
46 // Testing peer to access channel internals.
47 class SubchannelNodePeer {
48 public:
SubchannelNodePeer(SubchannelNode * node)49 explicit SubchannelNodePeer(SubchannelNode* node) : node_(node) {}
trace() const50 ChannelTrace* trace() const { return &node_->trace_; }
51
52 private:
53 SubchannelNode* node_;
54 };
55
GetSizeofTraceEvent()56 size_t GetSizeofTraceEvent() { return sizeof(ChannelTrace::TraceEvent); }
57
58 namespace {
59
60 MATCHER_P(IsJsonString, expected, "is JSON string") {
61 if (!::testing::ExplainMatchResult(Json::Type::kString, arg.type(),
62 result_listener)) {
63 return false;
64 }
65 return ::testing::ExplainMatchResult(expected, arg.string(), result_listener);
66 }
67
68 MATCHER_P(IsJsonStringNumber, expected, "is JSON string containing number") {
69 if (!::testing::ExplainMatchResult(Json::Type::kString, arg.type(),
70 result_listener)) {
71 return false;
72 }
73 int actual;
74 if (!absl::SimpleAtoi(arg.string(), &actual)) {
75 *result_listener << "JSON string \"" << arg.string()
76 << " does not contain numeric value";
77 return false;
78 }
79 return ::testing::ExplainMatchResult(expected, actual, result_listener);
80 }
81
82 MATCHER_P(IsJsonObject, matcher, "is JSON object") {
83 if (!::testing::ExplainMatchResult(Json::Type::kObject, arg.type(),
84 result_listener)) {
85 return false;
86 }
87 return ::testing::ExplainMatchResult(matcher, arg.object(), result_listener);
88 }
89
90 MATCHER_P(IsJsonArray, matcher, "is JSON array") {
91 if (!::testing::ExplainMatchResult(Json::Type::kArray, arg.type(),
92 result_listener)) {
93 return false;
94 }
95 return ::testing::ExplainMatchResult(matcher, arg.array(), result_listener);
96 }
97
98 MATCHER_P2(IsTraceEvent, description, severity, "is trace event") {
99 return ::testing::ExplainMatchResult(
100 IsJsonObject(::testing::ElementsAre(
101 ::testing::Pair("description", IsJsonString(description)),
102 ::testing::Pair("severity", IsJsonString(severity)),
103 ::testing::Pair("timestamp", IsJsonString(::testing::_)))),
104 arg, result_listener);
105 }
106
107 MATCHER_P3(IsTraceEventWithChannelRef, description, severity, channel_ref,
108 "is trace event with channel ref") {
109 return ::testing::ExplainMatchResult(
110 IsJsonObject(::testing::ElementsAre(
111 ::testing::Pair("channelRef",
112 IsJsonObject(::testing::ElementsAre(::testing::Pair(
113 "channelId", IsJsonStringNumber(channel_ref))))),
114 ::testing::Pair("description", IsJsonString(description)),
115 ::testing::Pair("severity", IsJsonString(severity)),
116 ::testing::Pair("timestamp", IsJsonString(::testing::_)))),
117 arg, result_listener);
118 }
119
120 MATCHER_P3(IsTraceEventWithSubchannelRef, description, severity, subchannel_ref,
121 "is trace event with subchannel ref") {
122 return ::testing::ExplainMatchResult(
123 IsJsonObject(::testing::ElementsAre(
124 ::testing::Pair("description", IsJsonString(description)),
125 ::testing::Pair("severity", IsJsonString(severity)),
126 ::testing::Pair(
127 "subchannelRef",
128 IsJsonObject(::testing::ElementsAre(::testing::Pair(
129 "subchannelId", IsJsonStringNumber(subchannel_ref))))),
130 ::testing::Pair("timestamp", IsJsonString(::testing::_)))),
131 arg, result_listener);
132 }
133
134 MATCHER(IsEmptyChannelTrace, "is empty channel trace") {
135 return ::testing::ExplainMatchResult(
136 IsJsonObject(::testing::ElementsAre(
137 ::testing::Pair("creationTimestamp", IsJsonString(::testing::_)))),
138 arg, result_listener);
139 }
140
141 MATCHER_P2(IsChannelTrace, num_events_logged_expected, events_matcher,
142 "is channel trace") {
143 return ::testing::ExplainMatchResult(
144 IsJsonObject(::testing::ElementsAre(
145 ::testing::Pair("creationTimestamp", IsJsonString(::testing::_)),
146 ::testing::Pair("events", IsJsonArray(events_matcher)),
147 ::testing::Pair("numEventsLogged",
148 IsJsonStringNumber(num_events_logged_expected)))),
149 arg, result_listener);
150 }
151
152 MATCHER_P(IsEmptyChannelTrace, num_events_logged_expected,
153 "is empty channel trace") {
154 return ::testing::ExplainMatchResult(
155 IsJsonObject(::testing::ElementsAre(
156 ::testing::Pair("creationTimestamp", IsJsonString(::testing::_)),
157 ::testing::Pair("numEventsLogged",
158 IsJsonStringNumber(num_events_logged_expected)))),
159 arg, result_listener);
160 }
161
ValidateJsonProtoTranslation(const Json & json)162 void ValidateJsonProtoTranslation(const Json& json) {
163 std::string json_str = JsonDump(json);
164 grpc::testing::ValidateChannelTraceProtoJsonTranslation(json_str);
165 }
166
167 } // anonymous namespace
168
169 const int kEventListMemoryLimit = 1024 * 1024;
170
171 // Tests basic ChannelTrace functionality like construction, adding trace, and
172 // lookups by uuid.
TEST(ChannelTracerTest,BasicTest)173 TEST(ChannelTracerTest, BasicTest) {
174 ExecCtx exec_ctx;
175 ChannelTrace tracer(kEventListMemoryLimit);
176 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
177 grpc_slice_from_static_string("one"));
178 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
179 grpc_slice_from_static_string("two"));
180 tracer.AddTraceEvent(ChannelTrace::Severity::Warning,
181 grpc_slice_from_static_string("three"));
182 tracer.AddTraceEvent(ChannelTrace::Severity::Error,
183 grpc_slice_from_static_string("four"));
184 Json json = tracer.RenderJson();
185 ValidateJsonProtoTranslation(json);
186 EXPECT_THAT(json, IsChannelTrace(4, ::testing::ElementsAre(
187 IsTraceEvent("one", "CT_INFO"),
188 IsTraceEvent("two", "CT_INFO"),
189 IsTraceEvent("three", "CT_WARNING"),
190 IsTraceEvent("four", "CT_ERROR"))))
191 << JsonDump(json);
192 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
193 grpc_slice_from_static_string("five"));
194 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
195 grpc_slice_from_static_string("six"));
196 json = tracer.RenderJson();
197 ValidateJsonProtoTranslation(json);
198 EXPECT_THAT(json, IsChannelTrace(6, ::testing::ElementsAre(
199 IsTraceEvent("one", "CT_INFO"),
200 IsTraceEvent("two", "CT_INFO"),
201 IsTraceEvent("three", "CT_WARNING"),
202 IsTraceEvent("four", "CT_ERROR"),
203 IsTraceEvent("five", "CT_INFO"),
204 IsTraceEvent("six", "CT_INFO"))))
205 << JsonDump(json);
206 }
207
208 // Tests more complex functionality, like a parent channel tracking
209 // subchannles. This exercises the ref/unref patterns since the parent tracer
210 // and this function will both hold refs to the subchannel.
TEST(ChannelTracerTest,ComplexTest)211 TEST(ChannelTracerTest, ComplexTest) {
212 ExecCtx exec_ctx;
213 ChannelTrace tracer(kEventListMemoryLimit);
214 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
215 grpc_slice_from_static_string("one"));
216 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
217 grpc_slice_from_static_string("two"));
218 auto subchannel_node = MakeRefCounted<SubchannelNode>("ipv4:1.2.3.4:5678",
219 kEventListMemoryLimit);
220 auto* subchannel_node_trace =
221 SubchannelNodePeer(subchannel_node.get()).trace();
222 tracer.AddTraceEventWithReference(
223 ChannelTrace::Severity::Info,
224 grpc_slice_from_static_string("subchannel one created"), subchannel_node);
225 Json json = tracer.RenderJson();
226 ValidateJsonProtoTranslation(json);
227 EXPECT_THAT(json,
228 IsChannelTrace(3, ::testing::ElementsAre(
229 IsTraceEvent("one", "CT_INFO"),
230 IsTraceEvent("two", "CT_INFO"),
231 IsTraceEventWithSubchannelRef(
232 "subchannel one created", "CT_INFO",
233 subchannel_node->uuid()))))
234 << JsonDump(json);
235 subchannel_node_trace->AddTraceEvent(ChannelTrace::Severity::Info,
236 grpc_slice_from_static_string("one"));
237 json = subchannel_node_trace->RenderJson();
238 ValidateJsonProtoTranslation(json);
239 EXPECT_THAT(
240 json,
241 IsChannelTrace(1, ::testing::ElementsAre(IsTraceEvent("one", "CT_INFO"))))
242 << JsonDump(json);
243 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
244 grpc_slice_from_static_string("three"));
245 auto channel_node =
246 MakeRefCounted<ChannelNode>("fake_target", kEventListMemoryLimit, 0);
247 tracer.AddTraceEventWithReference(
248 ChannelTrace::Severity::Info,
249 grpc_slice_from_static_string("LB channel two created"), channel_node);
250 tracer.AddTraceEventWithReference(
251 ChannelTrace::Severity::Warning,
252 grpc_slice_from_static_string("subchannel one inactive"),
253 subchannel_node);
254 json = tracer.RenderJson();
255 ValidateJsonProtoTranslation(json);
256 EXPECT_THAT(
257 json,
258 IsChannelTrace(
259 6,
260 ::testing::ElementsAre(
261 IsTraceEvent("one", "CT_INFO"), IsTraceEvent("two", "CT_INFO"),
262 IsTraceEventWithSubchannelRef("subchannel one created", "CT_INFO",
263 subchannel_node->uuid()),
264 IsTraceEvent("three", "CT_INFO"),
265 IsTraceEventWithChannelRef("LB channel two created", "CT_INFO",
266 channel_node->uuid()),
267 IsTraceEventWithSubchannelRef("subchannel one inactive",
268 "CT_WARNING",
269 subchannel_node->uuid()))))
270 << JsonDump(json);
271 }
272
TEST(ChannelTracerTest,TestSmallMemoryLimit)273 TEST(ChannelTracerTest, TestSmallMemoryLimit) {
274 ExecCtx exec_ctx;
275 // Doesn't make sense in practice, but serves a testing purpose for the
276 // channel tracing bookkeeping. All tracing events added should get
277 // immediately garbage collected.
278 const int kSmallMemoryLimit = 1;
279 ChannelTrace tracer(kSmallMemoryLimit);
280 const size_t kNumEvents = 4;
281 for (size_t i = 0; i < kNumEvents; ++i) {
282 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
283 grpc_slice_from_static_string("trace"));
284 }
285 Json json = tracer.RenderJson();
286 ValidateJsonProtoTranslation(json);
287 EXPECT_THAT(json, IsEmptyChannelTrace(kNumEvents)) << JsonDump(json);
288 }
289
TEST(ChannelTracerTest,TestEviction)290 TEST(ChannelTracerTest, TestEviction) {
291 ExecCtx exec_ctx;
292 const int kTraceEventSize = GetSizeofTraceEvent();
293 const int kNumEvents = 5;
294 ChannelTrace tracer(kTraceEventSize * kNumEvents);
295 std::list<::testing::Matcher<Json>> matchers;
296 for (int i = 1; i <= kNumEvents; ++i) {
297 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
298 grpc_slice_from_static_string("trace"));
299 matchers.push_back(IsTraceEvent("trace", "CT_INFO"));
300 Json json = tracer.RenderJson();
301 ValidateJsonProtoTranslation(json);
302 EXPECT_THAT(json, IsChannelTrace(i, ::testing::ElementsAreArray(matchers)))
303 << JsonDump(json);
304 }
305 // At this point the list is full, and each subsequent enntry will cause an
306 // eviction.
307 for (int i = 1; i <= kNumEvents; ++i) {
308 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
309 grpc_slice_from_static_string("new"));
310 matchers.pop_front();
311 matchers.push_back(IsTraceEvent("new", "CT_INFO"));
312 Json json = tracer.RenderJson();
313 ValidateJsonProtoTranslation(json);
314 EXPECT_THAT(json, IsChannelTrace(kNumEvents + i,
315 ::testing::ElementsAreArray(matchers)))
316 << JsonDump(json);
317 }
318 }
319
TEST(ChannelTracerTest,TestMultipleEviction)320 TEST(ChannelTracerTest, TestMultipleEviction) {
321 ExecCtx exec_ctx;
322 const size_t kTraceEventSize = GetSizeofTraceEvent();
323 const int kNumEvents = 5;
324 ChannelTrace tracer(kTraceEventSize * kNumEvents);
325 std::list<::testing::Matcher<Json>> matchers;
326 for (int i = 1; i <= kNumEvents; ++i) {
327 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
328 grpc_slice_from_static_string("trace"));
329 matchers.push_back(IsTraceEvent("trace", "CT_INFO"));
330 Json json = tracer.RenderJson();
331 ValidateJsonProtoTranslation(json);
332 EXPECT_THAT(json, IsChannelTrace(i, ::testing::ElementsAreArray(matchers)))
333 << JsonDump(json);
334 }
335 // At this point the list is full, and each subsequent enntry will cause an
336 // eviction. We will now add in a trace event that has a copied string. This
337 // uses more memory, so it will cause a double eviciction.
338 std::string msg(GRPC_SLICE_INLINED_SIZE + 1, 'x');
339 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
340 grpc_slice_from_cpp_string(msg));
341 matchers.pop_front();
342 matchers.pop_front();
343 matchers.push_back(IsTraceEvent(msg, "CT_INFO"));
344 Json json = tracer.RenderJson();
345 ValidateJsonProtoTranslation(json);
346 EXPECT_THAT(json, IsChannelTrace(kNumEvents + 1,
347 ::testing::ElementsAreArray(matchers)))
348 << JsonDump(json);
349 }
350
TEST(ChannelTracerTest,TestTotalEviction)351 TEST(ChannelTracerTest, TestTotalEviction) {
352 ExecCtx exec_ctx;
353 const int kTraceEventSize = GetSizeofTraceEvent();
354 const int kNumEvents = 5;
355 ChannelTrace tracer(kTraceEventSize * kNumEvents);
356 std::list<::testing::Matcher<Json>> matchers;
357 for (int i = 1; i <= kNumEvents; ++i) {
358 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
359 grpc_slice_from_static_string("trace"));
360 matchers.push_back(IsTraceEvent("trace", "CT_INFO"));
361 Json json = tracer.RenderJson();
362 ValidateJsonProtoTranslation(json);
363 EXPECT_THAT(json, IsChannelTrace(i, ::testing::ElementsAreArray(matchers)))
364 << JsonDump(json);
365 }
366 // At this point the list is full. Now we add such a big slice that
367 // everything gets evicted.
368 grpc_slice huge_slice = grpc_slice_malloc(kTraceEventSize * (kNumEvents + 1));
369 tracer.AddTraceEvent(ChannelTrace::Severity::Info, huge_slice);
370 Json json = tracer.RenderJson();
371 ValidateJsonProtoTranslation(json);
372 EXPECT_THAT(json, IsEmptyChannelTrace(kNumEvents + 1)) << JsonDump(json);
373 }
374
375 // Tests that the code is thread-safe.
TEST(ChannelTracerTest,ThreadSafety)376 TEST(ChannelTracerTest, ThreadSafety) {
377 ExecCtx exec_ctx;
378 ChannelTrace tracer(kEventListMemoryLimit);
379 absl::Notification done;
380 std::vector<std::unique_ptr<std::thread>> threads;
381 for (size_t i = 0; i < 10; ++i) {
382 threads.push_back(std::make_unique<std::thread>([&]() {
383 do {
384 tracer.AddTraceEvent(ChannelTrace::Severity::Info,
385 grpc_slice_from_static_string("trace"));
386 } while (!done.HasBeenNotified());
387 }));
388 }
389 for (size_t i = 0; i < 10; ++i) {
390 tracer.RenderJson();
391 }
392 done.Notify();
393 for (const auto& thd : threads) {
394 thd->join();
395 }
396 }
397
398 } // namespace testing
399 } // namespace channelz
400 } // namespace grpc_core
401
main(int argc,char ** argv)402 int main(int argc, char** argv) {
403 grpc::testing::TestEnvironment env(&argc, argv);
404 grpc_init();
405 ::testing::InitGoogleTest(&argc, argv);
406 int ret = RUN_ALL_TESTS();
407 grpc_shutdown();
408 return ret;
409 }
410