• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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