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