• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Abseil Authors
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 //      https://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 "absl/strings/internal/cordz_info.h"
16 
17 #include <vector>
18 
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include "absl/base/config.h"
22 #include "absl/debugging/stacktrace.h"
23 #include "absl/debugging/symbolize.h"
24 #include "absl/strings/cordz_test_helpers.h"
25 #include "absl/strings/internal/cord_rep_flat.h"
26 #include "absl/strings/internal/cordz_handle.h"
27 #include "absl/strings/internal/cordz_statistics.h"
28 #include "absl/strings/internal/cordz_update_tracker.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/types/span.h"
31 
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34 namespace cord_internal {
35 namespace {
36 
37 using ::testing::ElementsAre;
38 using ::testing::Eq;
39 using ::testing::HasSubstr;
40 using ::testing::Ne;
41 using ::testing::SizeIs;
42 
43 // Used test values
44 auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown;
45 auto constexpr kTrackCordMethod = CordzUpdateTracker::kConstructorString;
46 auto constexpr kChildMethod = CordzUpdateTracker::kConstructorCord;
47 auto constexpr kUpdateMethod = CordzUpdateTracker::kAppendString;
48 
49 // Local less verbose helper
DeleteQueue()50 std::vector<const CordzHandle*> DeleteQueue() {
51   return CordzHandle::DiagnosticsGetDeleteQueue();
52 }
53 
FormatStack(absl::Span<void * const> raw_stack)54 std::string FormatStack(absl::Span<void* const> raw_stack) {
55   static constexpr size_t buf_size = 1 << 14;
56   std::unique_ptr<char[]> buf(new char[buf_size]);
57   std::string output;
58   for (void* stackp : raw_stack) {
59     if (absl::Symbolize(stackp, buf.get(), buf_size)) {
60       absl::StrAppend(&output, "    ", buf.get(), "\n");
61     }
62   }
63   return output;
64 }
65 
TEST(CordzInfoTest,TrackCord)66 TEST(CordzInfoTest, TrackCord) {
67   TestCordData data;
68   CordzInfo::TrackCord(data.data, kTrackCordMethod);
69   CordzInfo* info = data.data.cordz_info();
70   ASSERT_THAT(info, Ne(nullptr));
71   EXPECT_FALSE(info->is_snapshot());
72   EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info));
73   EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep));
74   info->Untrack();
75 }
76 
TEST(CordzInfoTest,MaybeTrackChildCordWithoutSampling)77 TEST(CordzInfoTest, MaybeTrackChildCordWithoutSampling) {
78   CordzSamplingIntervalHelper sample_none(99999);
79   TestCordData parent, child;
80   CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
81   EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
82 }
83 
TEST(CordzInfoTest,MaybeTrackChildCordWithSampling)84 TEST(CordzInfoTest, MaybeTrackChildCordWithSampling) {
85   CordzSamplingIntervalHelper sample_all(1);
86   TestCordData parent, child;
87   CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
88   EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
89 }
90 
TEST(CordzInfoTest,MaybeTrackChildCordWithoutSamplingParentSampled)91 TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingParentSampled) {
92   CordzSamplingIntervalHelper sample_none(99999);
93   TestCordData parent, child;
94   CordzInfo::TrackCord(parent.data, kTrackCordMethod);
95   CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
96   CordzInfo* parent_info = parent.data.cordz_info();
97   CordzInfo* child_info = child.data.cordz_info();
98   ASSERT_THAT(child_info, Ne(nullptr));
99   EXPECT_THAT(child_info->GetCordRepForTesting(), Eq(child.rep.rep));
100   EXPECT_THAT(child_info->GetParentStack(), parent_info->GetStack());
101   parent_info->Untrack();
102   child_info->Untrack();
103 }
104 
TEST(CordzInfoTest,MaybeTrackChildCordWithoutSamplingChildSampled)105 TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingChildSampled) {
106   CordzSamplingIntervalHelper sample_none(99999);
107   TestCordData parent, child;
108   CordzInfo::TrackCord(child.data, kTrackCordMethod);
109   CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
110   EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
111 }
112 
TEST(CordzInfoTest,MaybeTrackChildCordWithSamplingChildSampled)113 TEST(CordzInfoTest, MaybeTrackChildCordWithSamplingChildSampled) {
114   CordzSamplingIntervalHelper sample_all(1);
115   TestCordData parent, child;
116   CordzInfo::TrackCord(child.data, kTrackCordMethod);
117   CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
118   EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
119 }
120 
TEST(CordzInfoTest,UntrackCord)121 TEST(CordzInfoTest, UntrackCord) {
122   TestCordData data;
123   CordzInfo::TrackCord(data.data, kTrackCordMethod);
124   CordzInfo* info = data.data.cordz_info();
125 
126   info->Untrack();
127   EXPECT_THAT(DeleteQueue(), SizeIs(0));
128 }
129 
TEST(CordzInfoTest,UntrackCordWithSnapshot)130 TEST(CordzInfoTest, UntrackCordWithSnapshot) {
131   TestCordData data;
132   CordzInfo::TrackCord(data.data, kTrackCordMethod);
133   CordzInfo* info = data.data.cordz_info();
134 
135   CordzSnapshot snapshot;
136   info->Untrack();
137   EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr));
138   EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep));
139   EXPECT_THAT(DeleteQueue(), ElementsAre(info, &snapshot));
140 }
141 
TEST(CordzInfoTest,SetCordRep)142 TEST(CordzInfoTest, SetCordRep) {
143   TestCordData data;
144   CordzInfo::TrackCord(data.data, kTrackCordMethod);
145   CordzInfo* info = data.data.cordz_info();
146 
147   TestCordRep rep;
148   info->Lock(CordzUpdateTracker::kAppendCord);
149   info->SetCordRep(rep.rep);
150   info->Unlock();
151   EXPECT_THAT(info->GetCordRepForTesting(), Eq(rep.rep));
152 
153   info->Untrack();
154 }
155 
TEST(CordzInfoTest,SetCordRepNullUntracksCordOnUnlock)156 TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) {
157   TestCordData data;
158   CordzInfo::TrackCord(data.data, kTrackCordMethod);
159   CordzInfo* info = data.data.cordz_info();
160 
161   info->Lock(CordzUpdateTracker::kAppendString);
162   info->SetCordRep(nullptr);
163   EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr));
164   EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(info));
165 
166   info->Unlock();
167   EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr));
168 }
169 
TEST(CordzInfoTest,RefCordRep)170 TEST(CordzInfoTest, RefCordRep) {
171   TestCordData data;
172   CordzInfo::TrackCord(data.data, kTrackCordMethod);
173   CordzInfo* info = data.data.cordz_info();
174 
175   size_t refcount = data.rep.rep->refcount.Get();
176   EXPECT_THAT(info->RefCordRep(), Eq(data.rep.rep));
177   EXPECT_THAT(data.rep.rep->refcount.Get(), Eq(refcount + 1));
178   CordRep::Unref(data.rep.rep);
179   info->Untrack();
180 }
181 
182 #if GTEST_HAS_DEATH_TEST
183 
TEST(CordzInfoTest,SetCordRepRequiresMutex)184 TEST(CordzInfoTest, SetCordRepRequiresMutex) {
185   TestCordData data;
186   CordzInfo::TrackCord(data.data, kTrackCordMethod);
187   CordzInfo* info = data.data.cordz_info();
188   TestCordRep rep;
189   EXPECT_DEBUG_DEATH(info->SetCordRep(rep.rep), ".*");
190   info->Untrack();
191 }
192 
193 #endif  // GTEST_HAS_DEATH_TEST
194 
TEST(CordzInfoTest,TrackUntrackHeadFirstV2)195 TEST(CordzInfoTest, TrackUntrackHeadFirstV2) {
196   CordzSnapshot snapshot;
197   EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
198 
199   TestCordData data;
200   CordzInfo::TrackCord(data.data, kTrackCordMethod);
201   CordzInfo* info1 = data.data.cordz_info();
202   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
203   EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
204 
205   TestCordData data2;
206   CordzInfo::TrackCord(data2.data, kTrackCordMethod);
207   CordzInfo* info2 = data2.data.cordz_info();
208   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
209   EXPECT_THAT(info2->Next(snapshot), Eq(info1));
210   EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
211 
212   info2->Untrack();
213   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
214   EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
215 
216   info1->Untrack();
217   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
218 }
219 
TEST(CordzInfoTest,TrackUntrackTailFirstV2)220 TEST(CordzInfoTest, TrackUntrackTailFirstV2) {
221   CordzSnapshot snapshot;
222   EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
223 
224   TestCordData data;
225   CordzInfo::TrackCord(data.data, kTrackCordMethod);
226   CordzInfo* info1 = data.data.cordz_info();
227   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
228   EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
229 
230   TestCordData data2;
231   CordzInfo::TrackCord(data2.data, kTrackCordMethod);
232   CordzInfo* info2 = data2.data.cordz_info();
233   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
234   EXPECT_THAT(info2->Next(snapshot), Eq(info1));
235   EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
236 
237   info1->Untrack();
238   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
239   EXPECT_THAT(info2->Next(snapshot), Eq(nullptr));
240 
241   info2->Untrack();
242   ASSERT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
243 }
244 
TEST(CordzInfoTest,StackV2)245 TEST(CordzInfoTest, StackV2) {
246   TestCordData data;
247   // kMaxStackDepth is intentionally less than 64 (which is the max depth that
248   // Cordz will record) because if the actual stack depth is over 64
249   // (which it is on Apple platforms) then the expected_stack will end up
250   // catching a few frames at the end that the actual_stack didn't get and
251   // it will no longer be subset. At the time of this writing 58 is the max
252   // that will allow this test to pass (with a minimum os version of iOS 9), so
253   // rounded down to 50 to hopefully not run into this in the future if Apple
254   // makes small modifications to its testing stack. 50 is sufficient to prove
255   // that we got a decent stack.
256   static constexpr int kMaxStackDepth = 50;
257   CordzInfo::TrackCord(data.data, kTrackCordMethod);
258   CordzInfo* info = data.data.cordz_info();
259   std::vector<void*> local_stack;
260   local_stack.resize(kMaxStackDepth);
261   // In some environments we don't get stack traces. For example in Android
262   // absl::GetStackTrace will return 0 indicating it didn't find any stack. The
263   // resultant formatted stack will be "", but that still equals the stack
264   // recorded in CordzInfo, which is also empty. The skip_count is 1 so that the
265   // line number of the current stack isn't included in the HasSubstr check.
266   local_stack.resize(absl::GetStackTrace(local_stack.data(), kMaxStackDepth,
267                                          /*skip_count=*/1));
268 
269   std::string got_stack = FormatStack(info->GetStack());
270   std::string expected_stack = FormatStack(local_stack);
271   // If TrackCord is inlined, got_stack should match expected_stack. If it isn't
272   // inlined, got_stack should include an additional frame not present in
273   // expected_stack. Either way, expected_stack should be a substring of
274   // got_stack.
275   EXPECT_THAT(got_stack, HasSubstr(expected_stack));
276 
277   info->Untrack();
278 }
279 
280 // Local helper functions to get different stacks for child and parent.
TrackChildCord(InlineData & data,const InlineData & parent)281 CordzInfo* TrackChildCord(InlineData& data, const InlineData& parent) {
282   CordzInfo::TrackCord(data, parent, kChildMethod);
283   return data.cordz_info();
284 }
TrackParentCord(InlineData & data)285 CordzInfo* TrackParentCord(InlineData& data) {
286   CordzInfo::TrackCord(data, kTrackCordMethod);
287   return data.cordz_info();
288 }
289 
TEST(CordzInfoTest,GetStatistics)290 TEST(CordzInfoTest, GetStatistics) {
291   TestCordData data;
292   CordzInfo* info = TrackParentCord(data.data);
293 
294   CordzStatistics statistics = info->GetCordzStatistics();
295   EXPECT_THAT(statistics.size, Eq(data.rep.rep->length));
296   EXPECT_THAT(statistics.method, Eq(kTrackCordMethod));
297   EXPECT_THAT(statistics.parent_method, Eq(kUnknownMethod));
298   EXPECT_THAT(statistics.update_tracker.Value(kTrackCordMethod), Eq(1));
299 
300   info->Untrack();
301 }
302 
TEST(CordzInfoTest,LockCountsMethod)303 TEST(CordzInfoTest, LockCountsMethod) {
304   TestCordData data;
305   CordzInfo* info = TrackParentCord(data.data);
306 
307   info->Lock(kUpdateMethod);
308   info->Unlock();
309   info->Lock(kUpdateMethod);
310   info->Unlock();
311 
312   CordzStatistics statistics = info->GetCordzStatistics();
313   EXPECT_THAT(statistics.update_tracker.Value(kUpdateMethod), Eq(2));
314 
315   info->Untrack();
316 }
317 
TEST(CordzInfoTest,FromParent)318 TEST(CordzInfoTest, FromParent) {
319   TestCordData parent;
320   TestCordData child;
321   CordzInfo* info_parent = TrackParentCord(parent.data);
322   CordzInfo* info_child = TrackChildCord(child.data, parent.data);
323 
324   std::string stack = FormatStack(info_parent->GetStack());
325   std::string parent_stack = FormatStack(info_child->GetParentStack());
326   EXPECT_THAT(stack, Eq(parent_stack));
327 
328   CordzStatistics statistics = info_child->GetCordzStatistics();
329   EXPECT_THAT(statistics.size, Eq(child.rep.rep->length));
330   EXPECT_THAT(statistics.method, Eq(kChildMethod));
331   EXPECT_THAT(statistics.parent_method, Eq(kTrackCordMethod));
332   EXPECT_THAT(statistics.update_tracker.Value(kChildMethod), Eq(1));
333 
334   info_parent->Untrack();
335   info_child->Untrack();
336 }
337 
338 }  // namespace
339 }  // namespace cord_internal
340 ABSL_NAMESPACE_END
341 }  // namespace absl
342