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