1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/profiler/metadata_recorder.h"
6
7 #include "base/ranges/algorithm.h"
8 #include "base/test/gtest_util.h"
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/abseil-cpp/absl/types/optional.h"
12
13 namespace base {
14
operator ==(const MetadataRecorder::Item & lhs,const MetadataRecorder::Item & rhs)15 bool operator==(const MetadataRecorder::Item& lhs,
16 const MetadataRecorder::Item& rhs) {
17 return lhs.name_hash == rhs.name_hash && lhs.value == rhs.value;
18 }
19
operator <(const MetadataRecorder::Item & lhs,const MetadataRecorder::Item & rhs)20 bool operator<(const MetadataRecorder::Item& lhs,
21 const MetadataRecorder::Item& rhs) {
22 return lhs.name_hash < rhs.name_hash;
23 }
24
TEST(MetadataRecorderTest,GetItems_Empty)25 TEST(MetadataRecorderTest, GetItems_Empty) {
26 MetadataRecorder recorder;
27 MetadataRecorder::ItemArray items;
28
29 EXPECT_EQ(0u, MetadataRecorder::MetadataProvider(&recorder,
30 PlatformThread::CurrentId())
31 .GetItems(&items));
32 EXPECT_EQ(0u, MetadataRecorder::MetadataProvider(&recorder,
33 PlatformThread::CurrentId())
34 .GetItems(&items));
35 }
36
TEST(MetadataRecorderTest,Set_NewNameHash)37 TEST(MetadataRecorderTest, Set_NewNameHash) {
38 MetadataRecorder recorder;
39
40 recorder.Set(10, absl::nullopt, absl::nullopt, 20);
41
42 MetadataRecorder::ItemArray items;
43 size_t item_count;
44 {
45 item_count = MetadataRecorder::MetadataProvider(&recorder,
46 PlatformThread::CurrentId())
47 .GetItems(&items);
48 ASSERT_EQ(1u, item_count);
49 EXPECT_EQ(10u, items[0].name_hash);
50 EXPECT_FALSE(items[0].key.has_value());
51 EXPECT_FALSE(items[0].thread_id.has_value());
52 EXPECT_EQ(20, items[0].value);
53 }
54
55 recorder.Set(20, absl::nullopt, absl::nullopt, 30);
56
57 {
58 item_count = MetadataRecorder::MetadataProvider(&recorder,
59 PlatformThread::CurrentId())
60 .GetItems(&items);
61 ASSERT_EQ(2u, item_count);
62 EXPECT_EQ(20u, items[1].name_hash);
63 EXPECT_FALSE(items[1].key.has_value());
64 EXPECT_FALSE(items[1].thread_id.has_value());
65 EXPECT_EQ(30, items[1].value);
66 }
67 }
68
TEST(MetadataRecorderTest,Set_ExistingNameNash)69 TEST(MetadataRecorderTest, Set_ExistingNameNash) {
70 MetadataRecorder recorder;
71 recorder.Set(10, absl::nullopt, absl::nullopt, 20);
72 recorder.Set(10, absl::nullopt, absl::nullopt, 30);
73
74 MetadataRecorder::ItemArray items;
75 size_t item_count =
76 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
77 .GetItems(&items);
78 ASSERT_EQ(1u, item_count);
79 EXPECT_EQ(10u, items[0].name_hash);
80 EXPECT_FALSE(items[0].key.has_value());
81 EXPECT_FALSE(items[0].thread_id.has_value());
82 EXPECT_EQ(30, items[0].value);
83 }
84
TEST(MetadataRecorderTest,Set_ReAddRemovedNameNash)85 TEST(MetadataRecorderTest, Set_ReAddRemovedNameNash) {
86 MetadataRecorder recorder;
87 MetadataRecorder::ItemArray items;
88 std::vector<MetadataRecorder::Item> expected;
89 for (size_t i = 0; i < items.size(); ++i) {
90 expected.emplace_back(i, absl::nullopt, absl::nullopt, 0);
91 recorder.Set(i, absl::nullopt, absl::nullopt, 0);
92 }
93
94 // By removing an item from a full recorder, re-setting the same item, and
95 // verifying that the item is returned, we can verify that the recorder is
96 // reusing the inactive slot for the same name hash instead of trying (and
97 // failing) to allocate a new slot.
98 recorder.Remove(3, absl::nullopt, absl::nullopt);
99 recorder.Set(3, absl::nullopt, absl::nullopt, 0);
100
101 size_t item_count =
102 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
103 .GetItems(&items);
104 EXPECT_EQ(items.size(), item_count);
105 EXPECT_THAT(expected, ::testing::UnorderedElementsAreArray(items));
106 }
107
TEST(MetadataRecorderTest,Set_AddPastMaxCount)108 TEST(MetadataRecorderTest, Set_AddPastMaxCount) {
109 MetadataRecorder recorder;
110 MetadataRecorder::ItemArray items;
111 for (size_t i = 0; i < items.size(); ++i) {
112 recorder.Set(i, absl::nullopt, absl::nullopt, 0);
113 }
114
115 // This should fail silently.
116 recorder.Set(items.size(), absl::nullopt, absl::nullopt, 0);
117 }
118
TEST(MetadataRecorderTest,Set_NulloptKeyIsIndependentOfNonNulloptKey)119 TEST(MetadataRecorderTest, Set_NulloptKeyIsIndependentOfNonNulloptKey) {
120 MetadataRecorder recorder;
121
122 recorder.Set(10, 100, absl::nullopt, 20);
123
124 MetadataRecorder::ItemArray items;
125 size_t item_count;
126 {
127 item_count = MetadataRecorder::MetadataProvider(&recorder,
128 PlatformThread::CurrentId())
129 .GetItems(&items);
130 ASSERT_EQ(1u, item_count);
131 EXPECT_EQ(10u, items[0].name_hash);
132 ASSERT_TRUE(items[0].key.has_value());
133 EXPECT_EQ(100, *items[0].key);
134 EXPECT_EQ(20, items[0].value);
135 }
136
137 recorder.Set(10, absl::nullopt, absl::nullopt, 30);
138
139 {
140 item_count = MetadataRecorder::MetadataProvider(&recorder,
141 PlatformThread::CurrentId())
142 .GetItems(&items);
143 ASSERT_EQ(2u, item_count);
144
145 EXPECT_EQ(10u, items[0].name_hash);
146 ASSERT_TRUE(items[0].key.has_value());
147 EXPECT_EQ(100, *items[0].key);
148 EXPECT_EQ(20, items[0].value);
149
150 EXPECT_EQ(10u, items[1].name_hash);
151 EXPECT_FALSE(items[1].key.has_value());
152 EXPECT_EQ(30, items[1].value);
153 }
154 }
155
TEST(MetadataRecorderTest,Set_ThreadIdIsScoped)156 TEST(MetadataRecorderTest, Set_ThreadIdIsScoped) {
157 MetadataRecorder recorder;
158
159 recorder.Set(10, absl::nullopt, PlatformThread::CurrentId(), 20);
160
161 MetadataRecorder::ItemArray items;
162 size_t item_count;
163 {
164 item_count = MetadataRecorder::MetadataProvider(&recorder,
165 PlatformThread::CurrentId())
166 .GetItems(&items);
167 ASSERT_EQ(1u, item_count);
168 EXPECT_EQ(10u, items[0].name_hash);
169 EXPECT_FALSE(items[0].key.has_value());
170 ASSERT_TRUE(items[0].thread_id.has_value());
171 EXPECT_EQ(PlatformThread::CurrentId(), *items[0].thread_id);
172 EXPECT_EQ(20, items[0].value);
173 }
174 {
175 item_count = MetadataRecorder::MetadataProvider(&recorder, kInvalidThreadId)
176 .GetItems(&items);
177 EXPECT_EQ(0U, item_count);
178 }
179 }
180
TEST(MetadataRecorderTest,Set_NulloptThreadAndNonNulloptThread)181 TEST(MetadataRecorderTest, Set_NulloptThreadAndNonNulloptThread) {
182 MetadataRecorder recorder;
183
184 recorder.Set(10, absl::nullopt, PlatformThread::CurrentId(), 20);
185 recorder.Set(10, absl::nullopt, absl::nullopt, 30);
186
187 MetadataRecorder::ItemArray items;
188 size_t item_count =
189 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
190 .GetItems(&items);
191 ASSERT_EQ(2u, item_count);
192 EXPECT_EQ(10u, items[0].name_hash);
193 EXPECT_FALSE(items[0].key.has_value());
194 ASSERT_TRUE(items[0].thread_id.has_value());
195 EXPECT_EQ(PlatformThread::CurrentId(), *items[0].thread_id);
196 EXPECT_EQ(20, items[0].value);
197
198 EXPECT_EQ(10u, items[1].name_hash);
199 EXPECT_FALSE(items[1].key.has_value());
200 EXPECT_FALSE(items[1].thread_id.has_value());
201 EXPECT_EQ(30, items[1].value);
202 }
203
TEST(MetadataRecorderTest,Remove)204 TEST(MetadataRecorderTest, Remove) {
205 MetadataRecorder recorder;
206 recorder.Set(10, absl::nullopt, absl::nullopt, 20);
207 recorder.Set(30, absl::nullopt, absl::nullopt, 40);
208 recorder.Set(50, absl::nullopt, absl::nullopt, 60);
209 recorder.Remove(30, absl::nullopt, absl::nullopt);
210
211 MetadataRecorder::ItemArray items;
212 size_t item_count =
213 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
214 .GetItems(&items);
215 ASSERT_EQ(2u, item_count);
216 EXPECT_EQ(10u, items[0].name_hash);
217 EXPECT_FALSE(items[0].key.has_value());
218 EXPECT_EQ(20, items[0].value);
219 EXPECT_EQ(50u, items[1].name_hash);
220 EXPECT_FALSE(items[1].key.has_value());
221 EXPECT_EQ(60, items[1].value);
222 }
223
TEST(MetadataRecorderTest,Remove_DoesntExist)224 TEST(MetadataRecorderTest, Remove_DoesntExist) {
225 MetadataRecorder recorder;
226 recorder.Set(10, absl::nullopt, absl::nullopt, 20);
227 recorder.Remove(20, absl::nullopt, absl::nullopt);
228
229 MetadataRecorder::ItemArray items;
230 size_t item_count =
231 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
232 .GetItems(&items);
233 ASSERT_EQ(1u, item_count);
234 EXPECT_EQ(10u, items[0].name_hash);
235 EXPECT_FALSE(items[0].key.has_value());
236 EXPECT_EQ(20, items[0].value);
237 }
238
TEST(MetadataRecorderTest,Remove_NulloptKeyIsIndependentOfNonNulloptKey)239 TEST(MetadataRecorderTest, Remove_NulloptKeyIsIndependentOfNonNulloptKey) {
240 MetadataRecorder recorder;
241
242 recorder.Set(10, 100, absl::nullopt, 20);
243 recorder.Set(10, absl::nullopt, absl::nullopt, 30);
244
245 recorder.Remove(10, absl::nullopt, absl::nullopt);
246
247 MetadataRecorder::ItemArray items;
248 size_t item_count =
249 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
250 .GetItems(&items);
251 ASSERT_EQ(1u, item_count);
252 EXPECT_EQ(10u, items[0].name_hash);
253 ASSERT_TRUE(items[0].key.has_value());
254 EXPECT_EQ(100, *items[0].key);
255 EXPECT_EQ(20, items[0].value);
256 }
257
TEST(MetadataRecorderTest,Remove_NulloptThreadIsIndependentOfNonNulloptThread)258 TEST(MetadataRecorderTest,
259 Remove_NulloptThreadIsIndependentOfNonNulloptThread) {
260 MetadataRecorder recorder;
261
262 recorder.Set(10, absl::nullopt, PlatformThread::CurrentId(), 20);
263 recorder.Set(10, absl::nullopt, absl::nullopt, 30);
264
265 recorder.Remove(10, absl::nullopt, absl::nullopt);
266
267 MetadataRecorder::ItemArray items;
268 size_t item_count =
269 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
270 .GetItems(&items);
271 ASSERT_EQ(1u, item_count);
272 EXPECT_EQ(10u, items[0].name_hash);
273 EXPECT_FALSE(items[0].key.has_value());
274 ASSERT_TRUE(items[0].thread_id.has_value());
275 EXPECT_EQ(PlatformThread::CurrentId(), *items[0].thread_id);
276 EXPECT_EQ(20, items[0].value);
277 }
278
TEST(MetadataRecorderTest,ReclaimInactiveSlots)279 TEST(MetadataRecorderTest, ReclaimInactiveSlots) {
280 MetadataRecorder recorder;
281
282 std::set<MetadataRecorder::Item> items_set;
283 // Fill up the metadata map.
284 for (size_t i = 0; i < MetadataRecorder::MAX_METADATA_COUNT; ++i) {
285 recorder.Set(i, absl::nullopt, absl::nullopt, i);
286 items_set.insert(MetadataRecorder::Item{i, absl::nullopt, absl::nullopt,
287 static_cast<int64_t>(i)});
288 }
289
290 // Remove every fourth entry to fragment the data.
291 size_t entries_removed = 0;
292 for (size_t i = 3; i < MetadataRecorder::MAX_METADATA_COUNT; i += 4) {
293 recorder.Remove(i, absl::nullopt, absl::nullopt);
294 ++entries_removed;
295 items_set.erase(MetadataRecorder::Item{i, absl::nullopt, absl::nullopt,
296 static_cast<int64_t>(i)});
297 }
298
299 // Ensure that the inactive slots are reclaimed to make room for more entries.
300 for (size_t i = 1; i <= entries_removed; ++i) {
301 recorder.Set(i * 100, absl::nullopt, absl::nullopt, i * 100);
302 items_set.insert(MetadataRecorder::Item{
303 i * 100, absl::nullopt, absl::nullopt, static_cast<int64_t>(i * 100)});
304 }
305
306 MetadataRecorder::ItemArray items_arr;
307 ranges::copy(items_set, items_arr.begin());
308
309 MetadataRecorder::ItemArray recorder_items;
310 size_t recorder_item_count =
311 MetadataRecorder::MetadataProvider(&recorder, PlatformThread::CurrentId())
312 .GetItems(&recorder_items);
313 EXPECT_EQ(recorder_item_count, MetadataRecorder::MAX_METADATA_COUNT);
314 EXPECT_THAT(recorder_items, ::testing::UnorderedElementsAreArray(items_arr));
315 }
316
317 } // namespace base
318