1 // Copyright 2018 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 "components/metrics/call_stacks/call_stack_profile_metadata.h"
6 
7 #include <tuple>
8 #include <utility>
9 
10 #include "base/ranges/algorithm.h"
11 #include "base/strings/strcat.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/metrics_proto/sampled_profile.pb.h"
15 
16 namespace metrics {
17 
18 namespace {
19 
20 // Expects that |expected_item| was applied to |samples| at |sample_index| and
21 // |metadata_index|. Because of the "edge-triggered" metadata encoding, this
22 // expectation will be valid for the first sample seeing the item only.
ExpectMetadataApplied(const base::MetadataRecorder::Item & expected_item,const google::protobuf::RepeatedPtrField<CallStackProfile::StackSample> & samples,int sample_index,int metadata_index,const google::protobuf::RepeatedField<uint64_t> & name_hashes)23 void ExpectMetadataApplied(
24     const base::MetadataRecorder::Item& expected_item,
25     const google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>&
26         samples,
27     int sample_index,
28     int metadata_index,
29     const google::protobuf::RepeatedField<uint64_t>& name_hashes) {
30   const std::string index_info =
31       base::StrCat({"at sample_index ", base::NumberToString(sample_index),
32                     ", metadata_index ", base::NumberToString(metadata_index)});
33   const int name_hash_index =
34       base::ranges::find(name_hashes, expected_item.name_hash) -
35       name_hashes.begin();
36   ASSERT_NE(name_hash_index, name_hashes.size()) << index_info;
37 
38   ASSERT_LT(sample_index, samples.size()) << index_info;
39   const CallStackProfile::StackSample& sample = samples[sample_index];
40   ASSERT_LT(metadata_index, sample.metadata_size()) << index_info;
41   const CallStackProfile::MetadataItem& item = sample.metadata(metadata_index);
42   EXPECT_EQ(name_hash_index, item.name_hash_index()) << index_info;
43 
44   EXPECT_EQ(expected_item.key.has_value(), item.has_key()) << index_info;
45   if (expected_item.key.has_value())
46     EXPECT_EQ(*expected_item.key, item.key()) << index_info;
47   EXPECT_EQ(expected_item.value, item.value()) << index_info;
48 }
49 
50 // Expects that the |item| was unapplied at |sample|. Because of the
51 // "edge-triggered" metadata encoding, this expectation will be valid for the
52 // sample following the last sample with the item only.
ExpectMetadataUnapplied(const base::MetadataRecorder::Item & expected_item,const google::protobuf::RepeatedPtrField<CallStackProfile::StackSample> & samples,int sample_index,int metadata_index,const google::protobuf::RepeatedField<uint64_t> & name_hashes)53 void ExpectMetadataUnapplied(
54     const base::MetadataRecorder::Item& expected_item,
55     const google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>&
56         samples,
57     int sample_index,
58     int metadata_index,
59     const google::protobuf::RepeatedField<uint64_t>& name_hashes) {
60   const std::string index_info =
61       base::StrCat({"at sample_index ", base::NumberToString(sample_index),
62                     ", metadata_index ", base::NumberToString(metadata_index)});
63   const int name_hash_index =
64       base::ranges::find(name_hashes, expected_item.name_hash) -
65       name_hashes.begin();
66   ASSERT_NE(name_hash_index, name_hashes.size()) << index_info;
67 
68   ASSERT_LT(sample_index, samples.size()) << index_info;
69   const CallStackProfile::StackSample& sample = samples[sample_index];
70   ASSERT_LT(metadata_index, sample.metadata_size()) << index_info;
71   const CallStackProfile::MetadataItem& item = sample.metadata(metadata_index);
72   EXPECT_EQ(name_hash_index, item.name_hash_index()) << index_info;
73 
74   EXPECT_EQ(expected_item.key.has_value(), item.has_key()) << index_info;
75   if (expected_item.key.has_value())
76     EXPECT_EQ(*expected_item.key, item.key()) << index_info;
77   EXPECT_FALSE(item.has_value()) << index_info;
78 }
79 
80 }  // namespace
81 
TEST(CallStackProfileMetadataTest,MetadataRecorder_NoItems)82 TEST(CallStackProfileMetadataTest, MetadataRecorder_NoItems) {
83   base::MetadataRecorder metadata_recorder;
84   CallStackProfileMetadata metadata;
85   google::protobuf::RepeatedField<uint64_t> name_hashes;
86 
87   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
88       &metadata_recorder, base::PlatformThread::CurrentId()));
89 
90   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
91       metadata.CreateSampleMetadata(&name_hashes);
92 
93   ASSERT_EQ(0, name_hashes.size());
94   ASSERT_EQ(0, items.size());
95 }
96 
TEST(CallStackProfileMetadataTest,MetadataRecorder_SetItem)97 TEST(CallStackProfileMetadataTest, MetadataRecorder_SetItem) {
98   base::MetadataRecorder metadata_recorder;
99   CallStackProfileMetadata metadata;
100   google::protobuf::RepeatedField<uint64_t> name_hashes;
101 
102   metadata_recorder.Set(100, std::nullopt, std::nullopt, 10);
103   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
104       &metadata_recorder, base::PlatformThread::CurrentId()));
105   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
106       metadata.CreateSampleMetadata(&name_hashes);
107 
108   ASSERT_EQ(1, name_hashes.size());
109   EXPECT_EQ(100u, name_hashes[0]);
110 
111   ASSERT_EQ(1, items.size());
112   EXPECT_EQ(0, items[0].name_hash_index());
113   EXPECT_FALSE(items[0].has_key());
114   EXPECT_EQ(10, items[0].value());
115 }
116 
TEST(CallStackProfileMetadataTest,MetadataRecorder_SetKeyedItem)117 TEST(CallStackProfileMetadataTest, MetadataRecorder_SetKeyedItem) {
118   base::MetadataRecorder metadata_recorder;
119   CallStackProfileMetadata metadata;
120   google::protobuf::RepeatedField<uint64_t> name_hashes;
121 
122   metadata_recorder.Set(100, 50, std::nullopt, 10);
123   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
124       &metadata_recorder, base::PlatformThread::CurrentId()));
125   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
126       metadata.CreateSampleMetadata(&name_hashes);
127 
128   ASSERT_EQ(1, name_hashes.size());
129   EXPECT_EQ(100u, name_hashes[0]);
130 
131   ASSERT_EQ(1, items.size());
132   EXPECT_EQ(0, items[0].name_hash_index());
133   EXPECT_TRUE(items[0].has_key());
134   EXPECT_EQ(50, items[0].key());
135   EXPECT_EQ(10, items[0].value());
136 }
137 
TEST(CallStackProfileMetadataTest,MetadataRecorder_SetThreadItem)138 TEST(CallStackProfileMetadataTest, MetadataRecorder_SetThreadItem) {
139   base::MetadataRecorder metadata_recorder;
140   CallStackProfileMetadata metadata;
141   google::protobuf::RepeatedField<uint64_t> name_hashes;
142 
143   metadata_recorder.Set(100, std::nullopt, base::PlatformThread::CurrentId(),
144                         10);
145   metadata_recorder.Set(100, std::nullopt, base::kInvalidThreadId, 20);
146   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
147       &metadata_recorder, base::PlatformThread::CurrentId()));
148   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
149       metadata.CreateSampleMetadata(&name_hashes);
150 
151   ASSERT_EQ(1, name_hashes.size());
152   EXPECT_EQ(100u, name_hashes[0]);
153 
154   ASSERT_EQ(1, items.size());
155   EXPECT_EQ(0, items[0].name_hash_index());
156   EXPECT_FALSE(items[0].has_key());
157   EXPECT_EQ(10, items[0].value());
158 }
159 
TEST(CallStackProfileMetadataTest,MetadataRecorder_RepeatItem)160 TEST(CallStackProfileMetadataTest, MetadataRecorder_RepeatItem) {
161   base::MetadataRecorder metadata_recorder;
162   CallStackProfileMetadata metadata;
163   google::protobuf::RepeatedField<uint64_t> name_hashes;
164 
165   metadata_recorder.Set(100, std::nullopt, std::nullopt, 10);
166   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
167       &metadata_recorder, base::PlatformThread::CurrentId()));
168   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
169 
170   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
171       &metadata_recorder, base::PlatformThread::CurrentId()));
172   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
173       metadata.CreateSampleMetadata(&name_hashes);
174 
175   // The second sample shouldn't have any metadata because it's all the same
176   // as the last sample.
177   EXPECT_EQ(1, name_hashes.size());
178   EXPECT_TRUE(items.empty());
179 }
180 
TEST(CallStackProfileMetadataTest,MetadataRecorder_RepeatKeyedItem)181 TEST(CallStackProfileMetadataTest, MetadataRecorder_RepeatKeyedItem) {
182   base::MetadataRecorder metadata_recorder;
183   CallStackProfileMetadata metadata;
184   google::protobuf::RepeatedField<uint64_t> name_hashes;
185 
186   metadata_recorder.Set(100, 50, std::nullopt, 10);
187   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
188       &metadata_recorder, base::PlatformThread::CurrentId()));
189   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
190 
191   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
192       &metadata_recorder, base::PlatformThread::CurrentId()));
193   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
194       metadata.CreateSampleMetadata(&name_hashes);
195 
196   // The second sample shouldn't have any metadata because it's all the same
197   // as the last sample.
198   EXPECT_EQ(1, name_hashes.size());
199   EXPECT_TRUE(items.empty());
200 }
201 
TEST(CallStackProfileMetadataTest,MetadataRecorder_ModifiedItem)202 TEST(CallStackProfileMetadataTest, MetadataRecorder_ModifiedItem) {
203   base::MetadataRecorder metadata_recorder;
204   CallStackProfileMetadata metadata;
205   google::protobuf::RepeatedField<uint64_t> name_hashes;
206 
207   metadata_recorder.Set(100, std::nullopt, std::nullopt, 10);
208   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
209       &metadata_recorder, base::PlatformThread::CurrentId()));
210   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
211 
212   metadata_recorder.Set(100, std::nullopt, std::nullopt, 11);
213   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
214       &metadata_recorder, base::PlatformThread::CurrentId()));
215   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
216       metadata.CreateSampleMetadata(&name_hashes);
217 
218   EXPECT_EQ(1, name_hashes.size());
219 
220   ASSERT_EQ(1, items.size());
221   EXPECT_EQ(0, items[0].name_hash_index());
222   EXPECT_FALSE(items[0].has_key());
223   EXPECT_EQ(11, items[0].value());
224 }
225 
TEST(CallStackProfileMetadataTest,MetadataRecorder_ModifiedKeyedItem)226 TEST(CallStackProfileMetadataTest, MetadataRecorder_ModifiedKeyedItem) {
227   base::MetadataRecorder metadata_recorder;
228   CallStackProfileMetadata metadata;
229   google::protobuf::RepeatedField<uint64_t> name_hashes;
230 
231   metadata_recorder.Set(100, 50, std::nullopt, 10);
232   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
233       &metadata_recorder, base::PlatformThread::CurrentId()));
234   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
235 
236   metadata_recorder.Set(100, 50, std::nullopt, 11);
237   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
238       &metadata_recorder, base::PlatformThread::CurrentId()));
239   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
240       metadata.CreateSampleMetadata(&name_hashes);
241 
242   EXPECT_EQ(1, name_hashes.size());
243 
244   ASSERT_EQ(1, items.size());
245   EXPECT_EQ(0, items[0].name_hash_index());
246   EXPECT_TRUE(items[0].has_key());
247   EXPECT_EQ(50, items[0].key());
248   EXPECT_EQ(11, items[0].value());
249 }
250 
TEST(CallStackProfileMetadataTest,MetadataRecorder_NewItem)251 TEST(CallStackProfileMetadataTest, MetadataRecorder_NewItem) {
252   base::MetadataRecorder metadata_recorder;
253   CallStackProfileMetadata metadata;
254   google::protobuf::RepeatedField<uint64_t> name_hashes;
255 
256   metadata_recorder.Set(100, std::nullopt, std::nullopt, 10);
257   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
258       &metadata_recorder, base::PlatformThread::CurrentId()));
259   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
260 
261   metadata_recorder.Set(101, std::nullopt, std::nullopt, 11);
262   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
263       &metadata_recorder, base::PlatformThread::CurrentId()));
264   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
265       metadata.CreateSampleMetadata(&name_hashes);
266 
267   ASSERT_EQ(2, name_hashes.size());
268   EXPECT_EQ(101u, name_hashes[1]);
269 
270   ASSERT_EQ(1, items.size());
271   EXPECT_EQ(1, items[0].name_hash_index());
272   EXPECT_FALSE(items[0].has_key());
273   EXPECT_EQ(11, items[0].value());
274 }
275 
TEST(CallStackProfileMetadataTest,MetadataRecorder_NewKeyedItem)276 TEST(CallStackProfileMetadataTest, MetadataRecorder_NewKeyedItem) {
277   base::MetadataRecorder metadata_recorder;
278   CallStackProfileMetadata metadata;
279   google::protobuf::RepeatedField<uint64_t> name_hashes;
280 
281   metadata_recorder.Set(100, 50, std::nullopt, 10);
282   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
283       &metadata_recorder, base::PlatformThread::CurrentId()));
284   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
285 
286   metadata_recorder.Set(101, 50, std::nullopt, 11);
287   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
288       &metadata_recorder, base::PlatformThread::CurrentId()));
289   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
290       metadata.CreateSampleMetadata(&name_hashes);
291 
292   ASSERT_EQ(2, name_hashes.size());
293   EXPECT_EQ(101u, name_hashes[1]);
294 
295   ASSERT_EQ(1, items.size());
296   EXPECT_EQ(1, items[0].name_hash_index());
297   EXPECT_TRUE(items[0].has_key());
298   EXPECT_EQ(50, items[0].key());
299   EXPECT_EQ(11, items[0].value());
300 }
301 
TEST(CallStackProfileMetadataTest,MetadataRecorder_RemovedItem)302 TEST(CallStackProfileMetadataTest, MetadataRecorder_RemovedItem) {
303   base::MetadataRecorder metadata_recorder;
304   CallStackProfileMetadata metadata;
305   google::protobuf::RepeatedField<uint64_t> name_hashes;
306 
307   metadata_recorder.Set(100, std::nullopt, std::nullopt, 10);
308   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
309       &metadata_recorder, base::PlatformThread::CurrentId()));
310   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
311 
312   metadata_recorder.Remove(100, std::nullopt, std::nullopt);
313   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
314       &metadata_recorder, base::PlatformThread::CurrentId()));
315   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
316       metadata.CreateSampleMetadata(&name_hashes);
317 
318   EXPECT_EQ(1, name_hashes.size());
319 
320   ASSERT_EQ(1, items.size());
321   EXPECT_EQ(0, items[0].name_hash_index());
322   EXPECT_FALSE(items[0].has_key());
323   EXPECT_FALSE(items[0].has_value());
324 }
325 
TEST(CallStackProfileMetadataTest,MetadataRecorder_RemovedKeyedItem)326 TEST(CallStackProfileMetadataTest, MetadataRecorder_RemovedKeyedItem) {
327   base::MetadataRecorder metadata_recorder;
328   CallStackProfileMetadata metadata;
329   google::protobuf::RepeatedField<uint64_t> name_hashes;
330 
331   metadata_recorder.Set(100, 50, std::nullopt, 10);
332   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
333       &metadata_recorder, base::PlatformThread::CurrentId()));
334   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
335 
336   metadata_recorder.Remove(100, 50, std::nullopt);
337   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
338       &metadata_recorder, base::PlatformThread::CurrentId()));
339   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
340       metadata.CreateSampleMetadata(&name_hashes);
341 
342   EXPECT_EQ(1, name_hashes.size());
343 
344   ASSERT_EQ(1, items.size());
345   EXPECT_EQ(0, items[0].name_hash_index());
346   EXPECT_TRUE(items[0].has_key());
347   EXPECT_EQ(50, items[0].key());
348   EXPECT_FALSE(items[0].has_value());
349 }
350 
TEST(CallStackProfileMetadataTest,MetadataRecorder_RemovedThreadItem)351 TEST(CallStackProfileMetadataTest, MetadataRecorder_RemovedThreadItem) {
352   base::MetadataRecorder metadata_recorder;
353   CallStackProfileMetadata metadata;
354   google::protobuf::RepeatedField<uint64_t> name_hashes;
355 
356   metadata_recorder.Set(100, std::nullopt, base::PlatformThread::CurrentId(),
357                         10);
358   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
359       &metadata_recorder, base::PlatformThread::CurrentId()));
360   (void)metadata.CreateSampleMetadata(&name_hashes);
361 
362   metadata_recorder.Remove(100, std::nullopt,
363                            base::PlatformThread::CurrentId());
364   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
365       &metadata_recorder, base::PlatformThread::CurrentId()));
366   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
367       metadata.CreateSampleMetadata(&name_hashes);
368 
369   EXPECT_EQ(1, name_hashes.size());
370 
371   ASSERT_EQ(1, items.size());
372   EXPECT_EQ(0, items[0].name_hash_index());
373   EXPECT_FALSE(items[0].has_key());
374   EXPECT_FALSE(items[0].has_value());
375 }
376 
TEST(CallStackProfileMetadataTest,MetadataRecorder_SetMixedUnkeyedAndKeyedItems)377 TEST(CallStackProfileMetadataTest,
378      MetadataRecorder_SetMixedUnkeyedAndKeyedItems) {
379   base::MetadataRecorder metadata_recorder;
380   CallStackProfileMetadata metadata;
381   google::protobuf::RepeatedField<uint64_t> name_hashes;
382 
383   metadata_recorder.Set(100, std::nullopt, std::nullopt, 20);
384   metadata_recorder.Set(100, 50, std::nullopt, 10);
385   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
386       &metadata_recorder, base::PlatformThread::CurrentId()));
387   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
388       metadata.CreateSampleMetadata(&name_hashes);
389 
390   ASSERT_EQ(2, items.size());
391 
392   EXPECT_EQ(0, items[0].name_hash_index());
393   EXPECT_FALSE(items[0].has_key());
394   EXPECT_EQ(20, items[0].value());
395 
396   EXPECT_EQ(0, items[1].name_hash_index());
397   EXPECT_TRUE(items[1].has_key());
398   EXPECT_EQ(50, items[1].key());
399   EXPECT_EQ(10, items[1].value());
400 }
401 
TEST(CallStackProfileMetadataTest,MetadataRecorder_RemoveMixedUnkeyedAndKeyedItems)402 TEST(CallStackProfileMetadataTest,
403      MetadataRecorder_RemoveMixedUnkeyedAndKeyedItems) {
404   base::MetadataRecorder metadata_recorder;
405   CallStackProfileMetadata metadata;
406   google::protobuf::RepeatedField<uint64_t> name_hashes;
407 
408   metadata_recorder.Set(100, std::nullopt, std::nullopt, 20);
409   metadata_recorder.Set(100, 50, std::nullopt, 10);
410   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
411       &metadata_recorder, base::PlatformThread::CurrentId()));
412   std::ignore = metadata.CreateSampleMetadata(&name_hashes);
413 
414   metadata_recorder.Remove(100, std::nullopt, std::nullopt);
415   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
416       &metadata_recorder, base::PlatformThread::CurrentId()));
417   google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem> items =
418       metadata.CreateSampleMetadata(&name_hashes);
419 
420   ASSERT_EQ(1, items.size());
421   EXPECT_EQ(0, items[0].name_hash_index());
422   EXPECT_FALSE(items[0].has_key());
423   EXPECT_FALSE(items[0].has_value());
424 }
425 
426 // Checks that applying metadata results in the expected application and removal
427 // of the metadata.
TEST(CallStackProfileMetadataTest,ApplyMetadata_Basic)428 TEST(CallStackProfileMetadataTest, ApplyMetadata_Basic) {
429   CallStackProfileMetadata metadata;
430   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
431       stack_samples;
432   google::protobuf::RepeatedField<uint64_t> name_hashes;
433 
434   for (int i = 0; i < 5; i++)
435     stack_samples.Add();
436 
437   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
438   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
439                          stack_samples.begin() + 4, &stack_samples,
440                          &name_hashes);
441 
442   ASSERT_EQ(1, name_hashes.size());
443   EXPECT_EQ(3u, name_hashes[0]);
444 
445   EXPECT_EQ(0, stack_samples[0].metadata_size());
446 
447   // One metadata item should be recorded when the metadata starts.
448   EXPECT_EQ(1, stack_samples[1].metadata_size());
449   ExpectMetadataApplied(item, stack_samples, 1, 0, name_hashes);
450 
451   EXPECT_EQ(0, stack_samples[2].metadata_size());
452   EXPECT_EQ(0, stack_samples[3].metadata_size());
453 
454   // And one item should be recorded without value after the metadata ends.
455   EXPECT_EQ(1, stack_samples[4].metadata_size());
456   ExpectMetadataUnapplied(item, stack_samples, 4, 0, name_hashes);
457 }
458 
459 // Checks that metadata items with different name hashes are applied
460 // independently.
TEST(CallStackProfileMetadataTest,ApplyMetadata_DifferentNameHashes)461 TEST(CallStackProfileMetadataTest, ApplyMetadata_DifferentNameHashes) {
462   CallStackProfileMetadata metadata;
463   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
464       stack_samples;
465   google::protobuf::RepeatedField<uint64_t> name_hashes;
466 
467   for (int i = 0; i < 5; i++)
468     stack_samples.Add();
469 
470   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
471   const base::MetadataRecorder::Item item2(4, 30, std::nullopt, 300);
472   metadata.ApplyMetadata(item1, stack_samples.begin() + 1,
473                          stack_samples.begin() + 4, &stack_samples,
474                          &name_hashes);
475   metadata.ApplyMetadata(item2, stack_samples.begin() + 1,
476                          stack_samples.begin() + 4, &stack_samples,
477                          &name_hashes);
478 
479   ASSERT_EQ(2, name_hashes.size());
480   EXPECT_EQ(3u, name_hashes[0]);
481   EXPECT_EQ(4u, name_hashes[1]);
482 
483   EXPECT_EQ(0, stack_samples[0].metadata_size());
484 
485   EXPECT_EQ(2, stack_samples[1].metadata_size());
486   ExpectMetadataApplied(item1, stack_samples, 1, 0, name_hashes);
487   ExpectMetadataApplied(item2, stack_samples, 1, 1, name_hashes);
488 
489   EXPECT_EQ(0, stack_samples[2].metadata_size());
490   EXPECT_EQ(0, stack_samples[3].metadata_size());
491 
492   EXPECT_EQ(2, stack_samples[4].metadata_size());
493   ExpectMetadataUnapplied(item1, stack_samples, 4, 0, name_hashes);
494   ExpectMetadataUnapplied(item2, stack_samples, 4, 1, name_hashes);
495 }
496 
497 // Checks that metadata items with different keys are applied independently.
TEST(CallStackProfileMetadataTest,ApplyMetadata_DifferentKeys)498 TEST(CallStackProfileMetadataTest, ApplyMetadata_DifferentKeys) {
499   CallStackProfileMetadata metadata;
500   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
501       stack_samples;
502   google::protobuf::RepeatedField<uint64_t> name_hashes;
503 
504   for (int i = 0; i < 5; i++)
505     stack_samples.Add();
506 
507   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
508   const base::MetadataRecorder::Item item2(3, 40, std::nullopt, 300);
509   const base::MetadataRecorder::Item item3(3, std::nullopt, std::nullopt, 300);
510   metadata.ApplyMetadata(item1, stack_samples.begin() + 1,
511                          stack_samples.begin() + 4, &stack_samples,
512                          &name_hashes);
513   metadata.ApplyMetadata(item2, stack_samples.begin() + 1,
514                          stack_samples.begin() + 4, &stack_samples,
515                          &name_hashes);
516   metadata.ApplyMetadata(item3, stack_samples.begin() + 1,
517                          stack_samples.begin() + 4, &stack_samples,
518                          &name_hashes);
519 
520   ASSERT_EQ(1, name_hashes.size());
521   EXPECT_EQ(3u, name_hashes[0]);
522 
523   EXPECT_EQ(0, stack_samples[0].metadata_size());
524 
525   EXPECT_EQ(3, stack_samples[1].metadata_size());
526   ExpectMetadataApplied(item1, stack_samples, 1, 0, name_hashes);
527   ExpectMetadataApplied(item2, stack_samples, 1, 1, name_hashes);
528   ExpectMetadataApplied(item3, stack_samples, 1, 2, name_hashes);
529 
530   EXPECT_EQ(0, stack_samples[2].metadata_size());
531   EXPECT_EQ(0, stack_samples[3].metadata_size());
532 
533   EXPECT_EQ(3, stack_samples[4].metadata_size());
534   ExpectMetadataUnapplied(item1, stack_samples, 4, 0, name_hashes);
535   ExpectMetadataUnapplied(item2, stack_samples, 4, 1, name_hashes);
536   ExpectMetadataUnapplied(item3, stack_samples, 4, 2, name_hashes);
537 }
538 
539 // Checks that applying to an empty range has no effect.
TEST(CallStackProfileMetadataTest,ApplyMetadata_EmptyRange)540 TEST(CallStackProfileMetadataTest, ApplyMetadata_EmptyRange) {
541   CallStackProfileMetadata metadata;
542   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
543       stack_samples;
544   google::protobuf::RepeatedField<uint64_t> name_hashes;
545 
546   for (int i = 0; i < 5; i++)
547     stack_samples.Add();
548 
549   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
550   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
551                          stack_samples.begin() + 1, &stack_samples,
552                          &name_hashes);
553 
554   EXPECT_EQ(0, name_hashes.size());
555 
556   for (int i = 0; i < 5; i++)
557     EXPECT_EQ(0, stack_samples[i].metadata_size());
558 }
559 
560 // Checks that applying metadata through the end is recorded as unapplied on the
561 // next sample taken.
TEST(CallStackProfileMetadataTest,ApplyMetadata_ThroughEnd)562 TEST(CallStackProfileMetadataTest, ApplyMetadata_ThroughEnd) {
563   CallStackProfileMetadata metadata;
564   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
565       stack_samples;
566   google::protobuf::RepeatedField<uint64_t> name_hashes;
567 
568   for (int i = 0; i < 5; i++)
569     stack_samples.Add();
570 
571   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
572   metadata.ApplyMetadata(item, stack_samples.begin() + 1, stack_samples.end(),
573                          &stack_samples, &name_hashes);
574 
575   ASSERT_EQ(1, name_hashes.size());
576   EXPECT_EQ(3u, name_hashes[0]);
577 
578   EXPECT_EQ(0, stack_samples[0].metadata_size());
579 
580   // One metadata item should be recorded when the metadata starts.
581   EXPECT_EQ(1, stack_samples[1].metadata_size());
582   ExpectMetadataApplied(item, stack_samples, 1, 0, name_hashes);
583 
584   EXPECT_EQ(0, stack_samples[2].metadata_size());
585   EXPECT_EQ(0, stack_samples[3].metadata_size());
586   EXPECT_EQ(0, stack_samples[4].metadata_size());
587 
588   base::MetadataRecorder metadata_recorder;
589   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
590       &metadata_recorder, base::PlatformThread::CurrentId()));
591   *stack_samples.Add()->mutable_metadata() =
592       metadata.CreateSampleMetadata(&name_hashes);
593 
594   // And the following sample should have the metadata unapplied.
595   ExpectMetadataUnapplied(item, stack_samples, 5, 0, name_hashes);
596 }
597 
598 // Checks that metadata is properly applied when mixing RecordMetadata and
599 // ApplyMetadata over the same samples.
TEST(CallStackProfileMetadataTest,ApplyMetadata_WithRecordMetadata)600 TEST(CallStackProfileMetadataTest, ApplyMetadata_WithRecordMetadata) {
601   base::MetadataRecorder metadata_recorder;
602   CallStackProfileMetadata metadata;
603   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
604       stack_samples;
605   google::protobuf::RepeatedField<uint64_t> name_hashes;
606 
607   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
608   const base::MetadataRecorder::Item item2(5, 50, std::nullopt, 500);
609 
610   stack_samples.Add();
611 
612   // Apply then remove item1.
613   metadata_recorder.Set(item1.name_hash, *item1.key, item1.thread_id,
614                         item1.value);
615   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
616       &metadata_recorder, base::PlatformThread::CurrentId()));
617   *stack_samples.Add()->mutable_metadata() =
618       metadata.CreateSampleMetadata(&name_hashes);
619 
620   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
621       &metadata_recorder, base::PlatformThread::CurrentId()));
622   *stack_samples.Add()->mutable_metadata() =
623       metadata.CreateSampleMetadata(&name_hashes);
624 
625   metadata_recorder.Remove(item1.name_hash, *item1.key, item1.thread_id);
626   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
627       &metadata_recorder, base::PlatformThread::CurrentId()));
628   *stack_samples.Add()->mutable_metadata() =
629       metadata.CreateSampleMetadata(&name_hashes);
630 
631   stack_samples.Add();
632 
633   ASSERT_EQ(5, stack_samples.size());
634 
635   // Apply item2.
636   metadata.ApplyMetadata(item2, stack_samples.begin() + 1,
637                          stack_samples.begin() + 4, &stack_samples,
638                          &name_hashes);
639 
640   ASSERT_EQ(2, name_hashes.size());
641   EXPECT_EQ(3u, name_hashes[0]);
642   EXPECT_EQ(5u, name_hashes[1]);
643 
644   EXPECT_EQ(0, stack_samples[0].metadata_size());
645 
646   // Each of the two items should be recorded when their metadata starts.
647   ASSERT_EQ(2, stack_samples[1].metadata_size());
648   ExpectMetadataApplied(item1, stack_samples, 1, 0, name_hashes);
649   ExpectMetadataApplied(item2, stack_samples, 1, 1, name_hashes);
650 
651   EXPECT_EQ(0, stack_samples[2].metadata_size());
652 
653   // The original item should still be present.
654   EXPECT_EQ(1, stack_samples[3].metadata_size());
655   ExpectMetadataUnapplied(item1, stack_samples, 3, 0, name_hashes);
656 
657   // And one item should be recorded without value after the metadata ends.
658   EXPECT_EQ(1, stack_samples[4].metadata_size());
659   ExpectMetadataUnapplied(item2, stack_samples, 4, 0, name_hashes);
660 }
661 
662 // Checks that metadata is properly applied when using ApplyMetadata while
663 // a RecordMetadata-applied value is active.
TEST(CallStackProfileMetadataTest,ApplyMetadata_WithActiveMetadata)664 TEST(CallStackProfileMetadataTest, ApplyMetadata_WithActiveMetadata) {
665   base::MetadataRecorder metadata_recorder;
666   CallStackProfileMetadata metadata;
667   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
668       stack_samples;
669   google::protobuf::RepeatedField<uint64_t> name_hashes;
670 
671   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
672   const base::MetadataRecorder::Item item2(3, 30, std::nullopt, 400);
673 
674   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
675       &metadata_recorder, base::PlatformThread::CurrentId()));
676   *stack_samples.Add()->mutable_metadata() =
677       metadata.CreateSampleMetadata(&name_hashes);
678 
679   // Record item1 on an ongoing basis via RecordMetadata.
680   metadata_recorder.Set(item1.name_hash, *item1.key, item1.thread_id,
681                         item1.value);
682   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
683       &metadata_recorder, base::PlatformThread::CurrentId()));
684   *stack_samples.Add()->mutable_metadata() =
685       metadata.CreateSampleMetadata(&name_hashes);
686 
687   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
688       &metadata_recorder, base::PlatformThread::CurrentId()));
689   *stack_samples.Add()->mutable_metadata() =
690       metadata.CreateSampleMetadata(&name_hashes);
691 
692   ASSERT_EQ(3, stack_samples.size());
693 
694   // Apply item2 via ApplyMetadata up to the last sample.
695   metadata.ApplyMetadata(item2, stack_samples.begin(), stack_samples.end(),
696                          &stack_samples, &name_hashes);
697 
698   ASSERT_EQ(1, name_hashes.size());
699   EXPECT_EQ(3u, name_hashes[0]);
700 
701   EXPECT_EQ(1, stack_samples[0].metadata_size());
702   ExpectMetadataApplied(item2, stack_samples, 0, 0, name_hashes);
703 
704   EXPECT_EQ(0, stack_samples[1].metadata_size());
705   EXPECT_EQ(0, stack_samples[2].metadata_size());
706 
707   // The next recorded sample should have item1 applied since it's still active.
708   metadata.RecordMetadata(base::MetadataRecorder::MetadataProvider(
709       &metadata_recorder, base::PlatformThread::CurrentId()));
710   *stack_samples.Add()->mutable_metadata() =
711       metadata.CreateSampleMetadata(&name_hashes);
712 
713   EXPECT_EQ(1, stack_samples[3].metadata_size());
714   ExpectMetadataApplied(item1, stack_samples, 3, 0, name_hashes);
715 }
716 
717 // Checks application of the same item across non-overlapping ranges.
TEST(CallStackProfileMetadataTest,ApplyMetadata_IndependentRanges)718 TEST(CallStackProfileMetadataTest, ApplyMetadata_IndependentRanges) {
719   CallStackProfileMetadata metadata;
720   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
721       stack_samples;
722   google::protobuf::RepeatedField<uint64_t> name_hashes;
723 
724   for (int i = 0; i < 5; i++)
725     stack_samples.Add();
726 
727   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
728 
729   // Apply metadata over two non-overlapping ranges.
730   metadata.ApplyMetadata(item, stack_samples.begin(), stack_samples.begin() + 2,
731                          &stack_samples, &name_hashes);
732 
733   metadata.ApplyMetadata(item, stack_samples.begin() + 3,
734                          stack_samples.begin() + 4, &stack_samples,
735                          &name_hashes);
736 
737   ASSERT_EQ(1, name_hashes.size());
738   EXPECT_EQ(3u, name_hashes[0]);
739 
740   EXPECT_EQ(1, stack_samples[0].metadata_size());
741   ExpectMetadataApplied(item, stack_samples, 0, 0, name_hashes);
742 
743   EXPECT_EQ(0, stack_samples[1].metadata_size());
744 
745   EXPECT_EQ(1, stack_samples[2].metadata_size());
746   ExpectMetadataUnapplied(item, stack_samples, 2, 0, name_hashes);
747 
748   EXPECT_EQ(1, stack_samples[3].metadata_size());
749   ExpectMetadataApplied(item, stack_samples, 3, 0, name_hashes);
750 
751   EXPECT_EQ(1, stack_samples[4].metadata_size());
752   ExpectMetadataUnapplied(item, stack_samples, 4, 0, name_hashes);
753 }
754 
755 // Checks application of the same item across back-to-back ranges. The common
756 // sample should not have a metadata item set because it's unnecessary.
TEST(CallStackProfileMetadataTest,ApplyMetadata_BackToBackRanges)757 TEST(CallStackProfileMetadataTest, ApplyMetadata_BackToBackRanges) {
758   CallStackProfileMetadata metadata;
759   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
760       stack_samples;
761   google::protobuf::RepeatedField<uint64_t> name_hashes;
762 
763   for (int i = 0; i < 5; i++)
764     stack_samples.Add();
765 
766   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
767 
768   // Apply metadata over two ranges where the second starts on the same sample
769   // that the first ends. This should result in one range covering both.
770   metadata.ApplyMetadata(item, stack_samples.begin(), stack_samples.begin() + 2,
771                          &stack_samples, &name_hashes);
772 
773   metadata.ApplyMetadata(item, stack_samples.begin() + 2,
774                          stack_samples.begin() + 4, &stack_samples,
775                          &name_hashes);
776 
777   ASSERT_EQ(1, name_hashes.size());
778   EXPECT_EQ(3u, name_hashes[0]);
779 
780   EXPECT_EQ(1, stack_samples[0].metadata_size());
781   ExpectMetadataApplied(item, stack_samples, 0, 0, name_hashes);
782 
783   EXPECT_EQ(0, stack_samples[1].metadata_size());
784   EXPECT_EQ(0, stack_samples[2].metadata_size());
785   EXPECT_EQ(0, stack_samples[3].metadata_size());
786 
787   EXPECT_EQ(1, stack_samples[4].metadata_size());
788   ExpectMetadataUnapplied(item, stack_samples, 4, 0, name_hashes);
789 }
790 
791 // Checks application of different values across back-to-back ranges. The common
792 // sample must have the second metadata value set.
TEST(CallStackProfileMetadataTest,ApplyMetadata_BackToBackRangesWithDifferentValues)793 TEST(CallStackProfileMetadataTest,
794      ApplyMetadata_BackToBackRangesWithDifferentValues) {
795   CallStackProfileMetadata metadata;
796   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
797       stack_samples;
798   google::protobuf::RepeatedField<uint64_t> name_hashes;
799 
800   for (int i = 0; i < 5; i++)
801     stack_samples.Add();
802 
803   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
804   const base::MetadataRecorder::Item item2(3, 30, std::nullopt, 400);
805 
806   metadata.ApplyMetadata(item1, stack_samples.begin(),
807                          stack_samples.begin() + 2, &stack_samples,
808                          &name_hashes);
809 
810   metadata.ApplyMetadata(item2, stack_samples.begin() + 2,
811                          stack_samples.begin() + 4, &stack_samples,
812                          &name_hashes);
813 
814   ASSERT_EQ(1, name_hashes.size());
815   EXPECT_EQ(3u, name_hashes[0]);
816 
817   EXPECT_EQ(1, stack_samples[0].metadata_size());
818   ExpectMetadataApplied(item1, stack_samples, 0, 0, name_hashes);
819 
820   EXPECT_EQ(0, stack_samples[1].metadata_size());
821 
822   EXPECT_EQ(1, stack_samples[2].metadata_size());
823   ExpectMetadataApplied(item2, stack_samples, 2, 0, name_hashes);
824 
825   EXPECT_EQ(0, stack_samples[3].metadata_size());
826 
827   EXPECT_EQ(1, stack_samples[4].metadata_size());
828   ExpectMetadataUnapplied(item2, stack_samples, 4, 0, name_hashes);
829 }
830 
831 // Checks application of the same item over a range within a range where the
832 // item was already set. No metadata changes should be recorded on the interior
833 // range because they are unnecessary.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateWithinExistingRange)834 TEST(CallStackProfileMetadataTest, ApplyMetadata_UpdateWithinExistingRange) {
835   CallStackProfileMetadata metadata;
836   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
837       stack_samples;
838   google::protobuf::RepeatedField<uint64_t> name_hashes;
839 
840   for (int i = 0; i < 5; i++)
841     stack_samples.Add();
842 
843   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
844 
845   metadata.ApplyMetadata(item, stack_samples.begin(), stack_samples.begin() + 4,
846                          &stack_samples, &name_hashes);
847 
848   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
849                          stack_samples.begin() + 3, &stack_samples,
850                          &name_hashes);
851 
852   ASSERT_EQ(1, name_hashes.size());
853   EXPECT_EQ(3u, name_hashes[0]);
854 
855   EXPECT_EQ(1, stack_samples[0].metadata_size());
856   ExpectMetadataApplied(item, stack_samples, 0, 0, name_hashes);
857 
858   EXPECT_EQ(0, stack_samples[1].metadata_size());
859   EXPECT_EQ(0, stack_samples[2].metadata_size());
860   EXPECT_EQ(0, stack_samples[3].metadata_size());
861 
862   EXPECT_EQ(1, stack_samples[4].metadata_size());
863   ExpectMetadataUnapplied(item, stack_samples, 4, 0, name_hashes);
864 }
865 
866 // Checks application of a second value over a range within a range where the
867 // first value was already set. Metadata changes for the second value must be
868 // recorded on the interior range.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateWithinExistingRangeWithDifferentValues)869 TEST(CallStackProfileMetadataTest,
870      ApplyMetadata_UpdateWithinExistingRangeWithDifferentValues) {
871   CallStackProfileMetadata metadata;
872   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
873       stack_samples;
874   google::protobuf::RepeatedField<uint64_t> name_hashes;
875 
876   for (int i = 0; i < 5; i++)
877     stack_samples.Add();
878 
879   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
880   const base::MetadataRecorder::Item item2(3, 30, std::nullopt, 400);
881 
882   // Apply metadata over a range, then over a range fully enclosed within the
883   // first one.
884   metadata.ApplyMetadata(item1, stack_samples.begin(),
885                          stack_samples.begin() + 4, &stack_samples,
886                          &name_hashes);
887 
888   metadata.ApplyMetadata(item2, stack_samples.begin() + 1,
889                          stack_samples.begin() + 3, &stack_samples,
890                          &name_hashes);
891 
892   ASSERT_EQ(1, name_hashes.size());
893   EXPECT_EQ(3u, name_hashes[0]);
894 
895   EXPECT_EQ(1, stack_samples[0].metadata_size());
896   ExpectMetadataApplied(item1, stack_samples, 0, 0, name_hashes);
897 
898   EXPECT_EQ(1, stack_samples[1].metadata_size());
899   ExpectMetadataApplied(item2, stack_samples, 1, 0, name_hashes);
900 
901   EXPECT_EQ(0, stack_samples[2].metadata_size());
902 
903   EXPECT_EQ(1, stack_samples[3].metadata_size());
904   ExpectMetadataApplied(item1, stack_samples, 3, 0, name_hashes);
905 
906   EXPECT_EQ(1, stack_samples[4].metadata_size());
907   ExpectMetadataUnapplied(item1, stack_samples, 4, 0, name_hashes);
908 }
909 
910 // Checks application of the same item over a range enclosing a range where the
911 // item was already set. No metadata changes should be recorded on the interior
912 // range because they are unnecessary.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateEnclosesExistingRange)913 TEST(CallStackProfileMetadataTest, ApplyMetadata_UpdateEnclosesExistingRange) {
914   CallStackProfileMetadata metadata;
915   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
916       stack_samples;
917   google::protobuf::RepeatedField<uint64_t> name_hashes;
918 
919   for (int i = 0; i < 5; i++)
920     stack_samples.Add();
921 
922   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
923 
924   // Apply metadata over a range, then over a range that fully encloses the
925   // first one.
926   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
927                          stack_samples.begin() + 3, &stack_samples,
928                          &name_hashes);
929 
930   metadata.ApplyMetadata(item, stack_samples.begin(), stack_samples.begin() + 4,
931                          &stack_samples, &name_hashes);
932 
933   ASSERT_EQ(1, name_hashes.size());
934   EXPECT_EQ(3u, name_hashes[0]);
935 
936   EXPECT_EQ(1, stack_samples[0].metadata_size());
937   ExpectMetadataApplied(item, stack_samples, 0, 0, name_hashes);
938 
939   EXPECT_EQ(0, stack_samples[1].metadata_size());
940   EXPECT_EQ(0, stack_samples[2].metadata_size());
941   EXPECT_EQ(0, stack_samples[3].metadata_size());
942 
943   EXPECT_EQ(1, stack_samples[4].metadata_size());
944   ExpectMetadataUnapplied(item, stack_samples, 4, 0, name_hashes);
945 }
946 
947 // Checks application of a second value over a range enclosing a range where the
948 // first value was already set. Metadata changes for both values must be
949 // recorded.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateEnclosesExistingRangeWithDifferentValues)950 TEST(CallStackProfileMetadataTest,
951      ApplyMetadata_UpdateEnclosesExistingRangeWithDifferentValues) {
952   CallStackProfileMetadata metadata;
953   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
954       stack_samples;
955   google::protobuf::RepeatedField<uint64_t> name_hashes;
956 
957   for (int i = 0; i < 5; i++)
958     stack_samples.Add();
959 
960   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
961   const base::MetadataRecorder::Item item2(3, 30, std::nullopt, 400);
962 
963   // Apply metadata over a range, then over a range that fully encloses the
964   // first one.
965   metadata.ApplyMetadata(item1, stack_samples.begin() + 1,
966                          stack_samples.begin() + 3, &stack_samples,
967                          &name_hashes);
968 
969   metadata.ApplyMetadata(item2, stack_samples.begin(),
970                          stack_samples.begin() + 4, &stack_samples,
971                          &name_hashes);
972 
973   ASSERT_EQ(1, name_hashes.size());
974   EXPECT_EQ(3u, name_hashes[0]);
975 
976   EXPECT_EQ(1, stack_samples[0].metadata_size());
977   ExpectMetadataApplied(item2, stack_samples, 0, 0, name_hashes);
978 
979   EXPECT_EQ(0, stack_samples[1].metadata_size());
980   EXPECT_EQ(0, stack_samples[2].metadata_size());
981   EXPECT_EQ(0, stack_samples[3].metadata_size());
982 
983   EXPECT_EQ(1, stack_samples[4].metadata_size());
984   ExpectMetadataUnapplied(item2, stack_samples, 4, 0, name_hashes);
985 }
986 
987 // Checks application of an item over a range overlapping the start (but not
988 // end) of a range where the item was already set. No metadata changes should be
989 // recorded on the interior application because it is unnecessary.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateOverlapsBegin)990 TEST(CallStackProfileMetadataTest, ApplyMetadata_UpdateOverlapsBegin) {
991   CallStackProfileMetadata metadata;
992   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
993       stack_samples;
994   google::protobuf::RepeatedField<uint64_t> name_hashes;
995 
996   for (int i = 0; i < 5; i++)
997     stack_samples.Add();
998 
999   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
1000 
1001   // Apply metadata over a range, then over a range that overlaps the beginning
1002   // (but not the end) of first one.
1003   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
1004                          stack_samples.begin() + 3, &stack_samples,
1005                          &name_hashes);
1006 
1007   metadata.ApplyMetadata(item, stack_samples.begin(), stack_samples.begin() + 2,
1008                          &stack_samples, &name_hashes);
1009 
1010   ASSERT_EQ(1, name_hashes.size());
1011   EXPECT_EQ(3u, name_hashes[0]);
1012 
1013   EXPECT_EQ(1, stack_samples[0].metadata_size());
1014   ExpectMetadataApplied(item, stack_samples, 0, 0, name_hashes);
1015 
1016   EXPECT_EQ(0, stack_samples[1].metadata_size());
1017   EXPECT_EQ(0, stack_samples[2].metadata_size());
1018 
1019   EXPECT_EQ(1, stack_samples[3].metadata_size());
1020   ExpectMetadataUnapplied(item, stack_samples, 3, 0, name_hashes);
1021 
1022   EXPECT_EQ(0, stack_samples[4].metadata_size());
1023 }
1024 
1025 // Checks application of a second different value over a range overlapping the
1026 // start (but not end) of a range where the first value was already
1027 // set. Metadata changes must be recorded on the interior application.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateOverlapsBeginWithDifferentValues)1028 TEST(CallStackProfileMetadataTest,
1029      ApplyMetadata_UpdateOverlapsBeginWithDifferentValues) {
1030   CallStackProfileMetadata metadata;
1031   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
1032       stack_samples;
1033   google::protobuf::RepeatedField<uint64_t> name_hashes;
1034 
1035   for (int i = 0; i < 5; i++)
1036     stack_samples.Add();
1037 
1038   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
1039   const base::MetadataRecorder::Item item2(3, 30, std::nullopt, 400);
1040 
1041   // Apply metadata over a range, then over a range that overlaps the beginning
1042   // (but not the end) of first one.
1043   metadata.ApplyMetadata(item1, stack_samples.begin() + 1,
1044                          stack_samples.begin() + 3, &stack_samples,
1045                          &name_hashes);
1046 
1047   metadata.ApplyMetadata(item2, stack_samples.begin(),
1048                          stack_samples.begin() + 2, &stack_samples,
1049                          &name_hashes);
1050 
1051   ASSERT_EQ(1, name_hashes.size());
1052   EXPECT_EQ(3u, name_hashes[0]);
1053 
1054   EXPECT_EQ(1, stack_samples[0].metadata_size());
1055   ExpectMetadataApplied(item2, stack_samples, 0, 0, name_hashes);
1056 
1057   EXPECT_EQ(0, stack_samples[1].metadata_size());
1058   EXPECT_EQ(1, stack_samples[2].metadata_size());
1059   ExpectMetadataApplied(item1, stack_samples, 2, 0, name_hashes);
1060 
1061   EXPECT_EQ(1, stack_samples[3].metadata_size());
1062   ExpectMetadataUnapplied(item1, stack_samples, 3, 0, name_hashes);
1063 
1064   EXPECT_EQ(0, stack_samples[4].metadata_size());
1065 }
1066 
1067 // Checks application of an item over a range overlapping the end (but not
1068 // start) of a range where the item was already set. No metadata changes should
1069 // be recorded on the interior application because it is unnecessary.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateOverlapsEnd)1070 TEST(CallStackProfileMetadataTest, ApplyMetadata_UpdateOverlapsEnd) {
1071   CallStackProfileMetadata metadata;
1072   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
1073       stack_samples;
1074   google::protobuf::RepeatedField<uint64_t> name_hashes;
1075 
1076   for (int i = 0; i < 5; i++)
1077     stack_samples.Add();
1078 
1079   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
1080 
1081   // Apply metadata over a range, then over a range that overlaps the beginning
1082   // (but not the end) of first one.
1083   metadata.ApplyMetadata(item, stack_samples.begin(), stack_samples.begin() + 2,
1084                          &stack_samples, &name_hashes);
1085 
1086   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
1087                          stack_samples.begin() + 4, &stack_samples,
1088                          &name_hashes);
1089 
1090   ASSERT_EQ(1, name_hashes.size());
1091   EXPECT_EQ(3u, name_hashes[0]);
1092 
1093   EXPECT_EQ(1, stack_samples[0].metadata_size());
1094   ExpectMetadataApplied(item, stack_samples, 0, 0, name_hashes);
1095 
1096   EXPECT_EQ(0, stack_samples[1].metadata_size());
1097   EXPECT_EQ(0, stack_samples[2].metadata_size());
1098   EXPECT_EQ(0, stack_samples[3].metadata_size());
1099 
1100   EXPECT_EQ(1, stack_samples[4].metadata_size());
1101   ExpectMetadataUnapplied(item, stack_samples, 4, 0, name_hashes);
1102 }
1103 
1104 // Checks application of a second different value over a range overlapping the
1105 // end (but not start) of a range where the first value was already
1106 // set. Metadata changes must be recorded on the interior application.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateOverlapsEndWithDifferentValues)1107 TEST(CallStackProfileMetadataTest,
1108      ApplyMetadata_UpdateOverlapsEndWithDifferentValues) {
1109   CallStackProfileMetadata metadata;
1110   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
1111       stack_samples;
1112   google::protobuf::RepeatedField<uint64_t> name_hashes;
1113 
1114   for (int i = 0; i < 5; i++)
1115     stack_samples.Add();
1116 
1117   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
1118   const base::MetadataRecorder::Item item2(3, 30, std::nullopt, 400);
1119 
1120   // Apply metadata over a range, then over a range that overlaps the beginning
1121   // (but not the end) of first one.
1122   metadata.ApplyMetadata(item1, stack_samples.begin(),
1123                          stack_samples.begin() + 2, &stack_samples,
1124                          &name_hashes);
1125 
1126   metadata.ApplyMetadata(item2, stack_samples.begin() + 1,
1127                          stack_samples.begin() + 4, &stack_samples,
1128                          &name_hashes);
1129 
1130   ASSERT_EQ(1, name_hashes.size());
1131   EXPECT_EQ(3u, name_hashes[0]);
1132 
1133   EXPECT_EQ(1, stack_samples[0].metadata_size());
1134   ExpectMetadataApplied(item1, stack_samples, 0, 0, name_hashes);
1135 
1136   EXPECT_EQ(1, stack_samples[1].metadata_size());
1137   ExpectMetadataApplied(item2, stack_samples, 1, 0, name_hashes);
1138 
1139   EXPECT_EQ(0, stack_samples[2].metadata_size());
1140   EXPECT_EQ(0, stack_samples[3].metadata_size());
1141 
1142   EXPECT_EQ(1, stack_samples[4].metadata_size());
1143   ExpectMetadataUnapplied(item2, stack_samples, 4, 0, name_hashes);
1144 }
1145 
1146 // Checks that updating the same range multiple times with the same item
1147 // produces the same result as what we'd expect with just one update.
TEST(CallStackProfileMetadataTest,ApplyMetadata_Update)1148 TEST(CallStackProfileMetadataTest, ApplyMetadata_Update) {
1149   CallStackProfileMetadata metadata;
1150   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
1151       stack_samples;
1152   google::protobuf::RepeatedField<uint64_t> name_hashes;
1153 
1154   for (int i = 0; i < 5; i++)
1155     stack_samples.Add();
1156 
1157   const base::MetadataRecorder::Item item(3, 30, std::nullopt, 300);
1158 
1159   // Apply metadata over the same range with one value, then a different value.
1160   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
1161                          stack_samples.begin() + 4, &stack_samples,
1162                          &name_hashes);
1163 
1164   metadata.ApplyMetadata(item, stack_samples.begin() + 1,
1165                          stack_samples.begin() + 4, &stack_samples,
1166                          &name_hashes);
1167 
1168   ASSERT_EQ(1, name_hashes.size());
1169   EXPECT_EQ(3u, name_hashes[0]);
1170 
1171   EXPECT_EQ(0, stack_samples[0].metadata_size());
1172 
1173   EXPECT_EQ(1, stack_samples[1].metadata_size());
1174   ExpectMetadataApplied(item, stack_samples, 1, 0, name_hashes);
1175 
1176   EXPECT_EQ(0, stack_samples[2].metadata_size());
1177   EXPECT_EQ(0, stack_samples[3].metadata_size());
1178 
1179   EXPECT_EQ(1, stack_samples[4].metadata_size());
1180   ExpectMetadataUnapplied(item, stack_samples, 4, 0, name_hashes);
1181 }
1182 
1183 // Checks that applying to the same range with a different value overwrites the
1184 // initial value.
TEST(CallStackProfileMetadataTest,ApplyMetadata_UpdateWithDifferentValues)1185 TEST(CallStackProfileMetadataTest, ApplyMetadata_UpdateWithDifferentValues) {
1186   CallStackProfileMetadata metadata;
1187   google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>
1188       stack_samples;
1189   google::protobuf::RepeatedField<uint64_t> name_hashes;
1190 
1191   for (int i = 0; i < 5; i++)
1192     stack_samples.Add();
1193 
1194   const base::MetadataRecorder::Item item1(3, 30, std::nullopt, 300);
1195   const base::MetadataRecorder::Item item2(3, 30, std::nullopt, 400);
1196 
1197   // Apply metadata over the same range with one value, then a different value.
1198   metadata.ApplyMetadata(item1, stack_samples.begin() + 1,
1199                          stack_samples.begin() + 4, &stack_samples,
1200                          &name_hashes);
1201 
1202   metadata.ApplyMetadata(item2, stack_samples.begin() + 1,
1203                          stack_samples.begin() + 4, &stack_samples,
1204                          &name_hashes);
1205 
1206   ASSERT_EQ(1, name_hashes.size());
1207   EXPECT_EQ(3u, name_hashes[0]);
1208 
1209   EXPECT_EQ(0, stack_samples[0].metadata_size());
1210 
1211   EXPECT_EQ(1, stack_samples[1].metadata_size());
1212   ExpectMetadataApplied(item2, stack_samples, 1, 0, name_hashes);
1213 
1214   EXPECT_EQ(0, stack_samples[2].metadata_size());
1215   EXPECT_EQ(0, stack_samples[3].metadata_size());
1216 
1217   EXPECT_EQ(1, stack_samples[4].metadata_size());
1218   ExpectMetadataUnapplied(item2, stack_samples, 4, 0, name_hashes);
1219 }
1220 
1221 }  // namespace metrics
1222