• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <fstream>
16 #include <memory>
17 #include <string>
18 
19 #include "gtest/gtest.h"
20 #include "absl/strings/str_cat.h"
21 #include "absl/time/clock.h"
22 #include "absl/time/time.h"
23 #include "fcp/base/monitoring.h"
24 #include "fcp/base/platform.h"
25 #include "fcp/base/scheduler.h"
26 #include "fcp/testing/testing.h"
27 #include "fcp/tracing/scoped_tracing_recorder.h"
28 #include "fcp/tracing/test/tracing_schema.h"
29 #include "fcp/tracing/text_tracing_recorder.h"
30 #include "re2/re2.h"
31 
32 constexpr char kBaselineDir[] = "fcp/tracing/test/testdata";
33 
34 namespace fcp {
35 namespace {
36 
37 // Replaces timestamp with ${TIME} and span ID with ${ID} in text trace output.
38 // Span IDs need to be replaced because of the lack of determinism in running
39 // multiple threads in parallel.
PostProcessOutput(std::string * input)40 inline bool PostProcessOutput(std::string* input) {
41   RE2 timestamp_and_id_pattern("\\d{4}-\\d{2}-\\d{2}T[[:^blank:]]*\\s\\d+");
42   return RE2::GlobalReplace(input, timestamp_and_id_pattern, "${TIME} ${ID}") >
43          0;
44 }
45 
GetOutFileName(int id)46 std::string GetOutFileName(int id) {
47   return ConcatPath(testing::TempDir(), absl::StrCat(TestName(), id, ".out"));
48 }
49 
GetBaselineFileName(int id)50 std::string GetBaselineFileName(int id) {
51   return ConcatPath(kBaselineDir, absl::StrCat(TestName(), id, ".baseline"));
52 }
53 
VerifyAgainstBaseline(int id)54 absl::StatusOr<std::string> VerifyAgainstBaseline(int id) {
55   // Reading out file
56   std::string report = ReadFileToString(GetOutFileName(id)).value();
57   EXPECT_TRUE(PostProcessOutput(&report));
58   // Producing report which is expected to precisely match .baseline file.
59   std::ostringstream expected;
60   expected << "" << std::endl;
61 
62   // Compare produced report with baseline.
63   std::string baseline_path = GetBaselineFileName(id);
64   return ::fcp::VerifyAgainstBaseline(baseline_path, report);
65 }
66 
67 // Verifies that thread local tracing recorder can be changed on the same
68 // thread.
TEST(Tracing,ChangeThreadLocal)69 TEST(Tracing, ChangeThreadLocal) {
70   const int kCount = 2;
71   for (int i = 0; i < kCount; i++) {
72     const int id = i + 1;
73     TextTracingRecorder local_recorder(GetOutFileName(id), absl::UTCTimeZone());
74     ScopedTracingRecorder scoped_recorder(&local_recorder);
75     TracingSpan<SpanWithId> inner(id);
76   }
77 
78   for (int i = 0; i < kCount; i++) {
79     const int id = i + 1;
80     auto status_s = VerifyAgainstBaseline(id);
81     ASSERT_TRUE(status_s.ok()) << status_s.status();
82     auto& diff = status_s.value();
83     if (!diff.empty()) {
84       FAIL() << diff;
85     }
86   }
87 }
88 
TEST(Tracing,PerThread)89 TEST(Tracing, PerThread) {
90   const int kThreadCount = 2;
91   auto scheduler = CreateThreadPoolScheduler(kThreadCount);
92 
93   for (int i = 0; i < kThreadCount; i++) {
94     scheduler->Schedule([&, i]() {
95       const int id = i + 1;
96       TextTracingRecorder local_recorder(GetOutFileName(id),
97                                          absl::UTCTimeZone());
98       ScopedTracingRecorder scoped_recorder(&local_recorder);
99       TracingSpan<SpanWithId> inner(id);
100       for (int k = 0; k < 5; k++) {
101         absl::SleepFor(absl::Milliseconds(10));
102         Trace<EventFoo>(id * 11, id * 111);
103       }
104     });
105   }
106 
107   scheduler->WaitUntilIdle();
108 
109   for (int i = 0; i < kThreadCount; i++) {
110     const int id = i + 1;
111     auto status_s = VerifyAgainstBaseline(id);
112     ASSERT_TRUE(status_s.ok()) << status_s.status();
113     auto& diff = status_s.value();
114     if (!diff.empty()) {
115       FAIL() << diff;
116     }
117   }
118 }
119 
TEST(Tracing,UninstallRequired)120 TEST(Tracing, UninstallRequired) {
121   auto local_recorder =
122       std::make_shared<TextTracingRecorder>(absl::UTCTimeZone());
123   local_recorder->InstallAsThreadLocal();
124   ASSERT_DEATH(
125       local_recorder.reset(),
126       "Trace recorder must not be set as thread local at destruction time");
127   // Note that ASSERT_DEATH statement above runs in a separate process so it is
128   // still OK to uninstall the trace recorder here; otherwise this process
129   // would crash too on destruction of the trace recorder.
130   local_recorder->UninstallAsThreadLocal();
131 }
132 
133 // Tests that setting the same tracing recorder is OK and that the number of
134 // InstallAsThreadLocal and UninstallAsThreadLocal must be matching.
TEST(Tracing,ReentrancySuccess)135 TEST(Tracing, ReentrancySuccess) {
136   auto local_recorder =
137       std::make_shared<TextTracingRecorder>(absl::UTCTimeZone());
138   local_recorder->InstallAsThreadLocal();
139   local_recorder->InstallAsThreadLocal();
140   local_recorder->UninstallAsThreadLocal();
141   local_recorder->UninstallAsThreadLocal();
142 }
143 
144 // Verifies that not matching the number of InstallAsThreadLocal with
145 // UninstallAsThreadLocal results in a failure.
TEST(Tracing,ReentrancyFailure)146 TEST(Tracing, ReentrancyFailure) {
147   auto local_recorder =
148       std::make_shared<TextTracingRecorder>(absl::UTCTimeZone());
149   local_recorder->InstallAsThreadLocal();
150   // This simulates re-entracy by setting the same tracing recorder as
151   // thread local again.
152   local_recorder->InstallAsThreadLocal();
153   local_recorder->UninstallAsThreadLocal();
154   // At this point UninstallAsThreadLocal has been called only once, which isn't
155   // sufficient.
156   ASSERT_DEATH(
157       local_recorder.reset(),
158       "Trace recorder must not be set as thread local at destruction time");
159   // Note that ASSERT_DEATH statement above runs in a separate process so it is
160   // still necessary to uninstall the trace recorder here to make sure that
161   // the test doesn't crash in the main test process.
162   local_recorder->UninstallAsThreadLocal();
163 }
164 
165 // Test that changing per-thread tracing recorder isn't allowed without
166 // uninstalling first.
TEST(Tracing,ChangingThreadLocalRecorderFails)167 TEST(Tracing, ChangingThreadLocalRecorderFails) {
168   TextTracingRecorder local_recorder1(absl::UTCTimeZone());
169   TextTracingRecorder local_recorder2(absl::UTCTimeZone());
170   local_recorder1.InstallAsThreadLocal();
171   ASSERT_DEATH(local_recorder2.InstallAsThreadLocal(),
172                "Only one tracing recorder instance per thread is supported");
173   // Note that ASSERT_DEATH statement above runs in a separate process so
174   // uninstalling local_recorder1 is still needed in the main test process.
175   local_recorder1.UninstallAsThreadLocal();
176 }
177 
TEST(Tracing,UninstallingWrongThreadLocalRecorderFails)178 TEST(Tracing, UninstallingWrongThreadLocalRecorderFails) {
179   TextTracingRecorder local_recorder1(absl::UTCTimeZone());
180   TextTracingRecorder local_recorder2(absl::UTCTimeZone());
181   local_recorder1.InstallAsThreadLocal();
182   ASSERT_DEATH(local_recorder2.UninstallAsThreadLocal(),
183                "Attempting to uninstall thread local tracing recorder that "
184                "isn't currently installed");
185   // Note that ASSERT_DEATH statement above runs in a separate process so
186   // uninstalling local_recorder1 is still needed in the main test process.
187   local_recorder1.UninstallAsThreadLocal();
188 }
189 
190 }  // namespace
191 }  // namespace fcp
192