• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/serialization/serialization_utils.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include "base/check.h"
11 #include "base/containers/span.h"
12 #include "base/files/file.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/test/metrics/histogram_tester.h"
17 #include "components/metrics/serialization/metric_sample.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 namespace metrics {
22 namespace {
23 
24 using ::testing::IsEmpty;
25 
26 class SerializationUtilsTest : public testing::Test {
27  protected:
SerializationUtilsTest()28   SerializationUtilsTest() {
29     bool success = temporary_dir_.CreateUniqueTempDir();
30     if (success) {
31       base::FilePath dir_path = temporary_dir_.GetPath();
32       filename_ = dir_path.value() + "chromeossampletest";
33       filepath_ = base::FilePath(filename_);
34     }
35   }
36 
SetUp()37   void SetUp() override { base::DeleteFile(filepath_); }
38 
TestSerialization(MetricSample * sample)39   void TestSerialization(MetricSample* sample) {
40     std::string serialized(sample->ToString());
41     ASSERT_EQ('\0', serialized.back());
42     std::unique_ptr<MetricSample> deserialized =
43         SerializationUtils::ParseSample(serialized);
44     ASSERT_TRUE(deserialized);
45     EXPECT_TRUE(sample->IsEqual(*deserialized.get()));
46   }
47 
filename() const48   const std::string& filename() const { return filename_; }
filepath() const49   const base::FilePath& filepath() const { return filepath_; }
50 
51  private:
52   std::string filename_;
53   base::ScopedTempDir temporary_dir_;
54   base::FilePath filepath_;
55 };
56 
TEST_F(SerializationUtilsTest,CrashSerializeTest)57 TEST_F(SerializationUtilsTest, CrashSerializeTest) {
58   // Should work with both 1 and non-1 values
59   TestSerialization(MetricSample::CrashSample("test", /*num_samples=*/1).get());
60   TestSerialization(
61       MetricSample::CrashSample("test", /*num_samples=*/10).get());
62 }
63 
TEST_F(SerializationUtilsTest,HistogramSerializeTest)64 TEST_F(SerializationUtilsTest, HistogramSerializeTest) {
65   TestSerialization(MetricSample::HistogramSample(
66                         "myhist", /*sample=*/13, /*min=*/1, /*max=*/100,
67                         /*bucket_count=*/10, /*num_samples=*/1)
68                         .get());
69   TestSerialization(MetricSample::HistogramSample(
70                         "myhist", /*sample=*/13, /*min=*/1, /*max=*/100,
71                         /*bucket_count=*/10, /*num_samples=*/2)
72                         .get());
73 }
74 
TEST_F(SerializationUtilsTest,LinearSerializeTest)75 TEST_F(SerializationUtilsTest, LinearSerializeTest) {
76   TestSerialization(
77       MetricSample::LinearHistogramSample("linearhist", /*sample=*/12,
78                                           /*max=*/30, /*num_samples=*/1)
79           .get());
80   TestSerialization(
81       MetricSample::LinearHistogramSample("linearhist", /*sample=*/12,
82                                           /*max=*/30, /*num_samples=*/10)
83           .get());
84 }
85 
TEST_F(SerializationUtilsTest,SparseSerializeTest)86 TEST_F(SerializationUtilsTest, SparseSerializeTest) {
87   TestSerialization(MetricSample::SparseHistogramSample(
88                         "mysparse", /*sample=*/30, /*num_samples=*/1)
89                         .get());
90   TestSerialization(MetricSample::SparseHistogramSample(
91                         "mysparse", /*sample=*/30, /*num_samples=*/10)
92                         .get());
93 }
94 
TEST_F(SerializationUtilsTest,UserActionSerializeTest)95 TEST_F(SerializationUtilsTest, UserActionSerializeTest) {
96   TestSerialization(
97       MetricSample::UserActionSample("myaction", /*num_samples=*/1).get());
98   TestSerialization(
99       MetricSample::UserActionSample("myaction", /*num_samples=*/10).get());
100 }
101 
TEST_F(SerializationUtilsTest,InvalidCrashSerialize)102 TEST_F(SerializationUtilsTest, InvalidCrashSerialize) {
103   // No name
104   EXPECT_EQ(nullptr, MetricSample::ParseCrash(""));
105   // Empty name
106   EXPECT_EQ(nullptr, MetricSample::ParseCrash(" "));
107   // num_samples is not a number
108   EXPECT_EQ(nullptr, MetricSample::ParseCrash("kernel asdf"));
109   // Too many numbers
110   EXPECT_EQ(nullptr, MetricSample::ParseCrash("kernel 1 2"));
111   // Negative num_samples
112   EXPECT_EQ(nullptr, MetricSample::ParseCrash("kernel -1"));
113 }
114 
TEST_F(SerializationUtilsTest,InvalidHistogramSample)115 TEST_F(SerializationUtilsTest, InvalidHistogramSample) {
116   // Too few parts
117   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3"));
118   // Too many parts
119   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 4 5 6"));
120   // Empty hist name
121   EXPECT_EQ(nullptr, MetricSample::ParseHistogram(" 1 2 3 4 5"));
122   // sample is not a number
123   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist a 2 3 4 5"));
124   // min is not a number
125   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 a 3 4 5"));
126   // max is not a number
127   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 a 4 5"));
128   // buckets is not a number
129   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 a 5"));
130   // num_samples is not a number
131   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 4 a"));
132   // Negative num_samples
133   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 4 -1"));
134 }
135 
TEST_F(SerializationUtilsTest,InvalidSparseHistogramSample)136 TEST_F(SerializationUtilsTest, InvalidSparseHistogramSample) {
137   // Too few fields
138   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name"));
139   // Too many fields
140   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name 1 2 3"));
141   // No name
142   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram(" 1 2"));
143   // Invalid sample
144   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name a 2"));
145   // Invalid num_samples
146   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name 1 a"));
147   // Negative num_samples
148   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name 1 -1"));
149 }
150 
TEST_F(SerializationUtilsTest,InvalidLinearHistogramSample)151 TEST_F(SerializationUtilsTest, InvalidLinearHistogramSample) {
152   // Too few fields
153   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1"));
154   // Too many fields
155   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 2 3 4"));
156   // No name
157   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram(" 1 2 3"));
158   // Invalid sample
159   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name a 2 3"));
160   // Invalid max
161   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 a 3"));
162   // Invalid num_samples
163   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 2 a"));
164   // Negative num_samples
165   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 2 -1"));
166 }
167 
TEST_F(SerializationUtilsTest,InvalidUserAction)168 TEST_F(SerializationUtilsTest, InvalidUserAction) {
169   // Too few fields
170   EXPECT_EQ(nullptr, MetricSample::ParseUserAction(""));
171   // Too many fields
172   EXPECT_EQ(nullptr, MetricSample::ParseUserAction("name 1 2"));
173   // No name
174   EXPECT_EQ(nullptr, MetricSample::ParseUserAction(" 1"));
175   // Invalid num_samples
176   EXPECT_EQ(nullptr, MetricSample::ParseUserAction("name a"));
177   // Negative num_samples
178   EXPECT_EQ(nullptr, MetricSample::ParseUserAction("name -1"));
179 }
180 
TEST_F(SerializationUtilsTest,IllegalNameAreFilteredTest)181 TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) {
182   std::unique_ptr<MetricSample> sample1 = MetricSample::SparseHistogramSample(
183       "no space", /*sample=*/10, /*num_samples=*/1);
184   std::unique_ptr<MetricSample> sample2 = MetricSample::LinearHistogramSample(
185       base::StringPrintf("here%cbhe", '\0'), /*sample=*/1, /*max=*/3,
186       /*num_samples=*/2);
187 
188   EXPECT_FALSE(
189       SerializationUtils::WriteMetricToFile(*sample1.get(), filename()));
190   EXPECT_FALSE(
191       SerializationUtils::WriteMetricToFile(*sample2.get(), filename()));
192 
193   ASSERT_FALSE(base::PathExists(filepath()));
194 }
195 
TEST_F(SerializationUtilsTest,BadInputIsCaughtTest)196 TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) {
197   std::string input(
198       base::StringPrintf("sparsehistogram%cname foo%c1", '\0', '\0'));
199   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram(input).get());
200 }
201 
TEST_F(SerializationUtilsTest,MessageSeparatedByZero)202 TEST_F(SerializationUtilsTest, MessageSeparatedByZero) {
203   std::unique_ptr<MetricSample> crash =
204       MetricSample::CrashSample("mycrash", /*num_samples=*/10);
205 
206   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
207   std::optional<int64_t> size = base::GetFileSize(filepath());
208 
209   ASSERT_TRUE(size.has_value());
210   // 4 bytes for the size
211   // 5 bytes for crash
212   // 1 byte for \0
213   // 7 bytes for mycrash
214   // 3 bytes for " 10"
215   // 1 byte for \0
216   // -> total of 21
217   EXPECT_EQ(size.value(), 21);
218 }
219 
TEST_F(SerializationUtilsTest,MessagesTooLongAreDiscardedTest)220 TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) {
221   // Creates a message that is bigger than the maximum allowed size.
222   // As we are adding extra character (crash, \0s, etc), if the name is
223   // kMessageMaxLength long, it will be too long.
224   std::string name(SerializationUtils::kMessageMaxLength, 'c');
225 
226   std::unique_ptr<MetricSample> crash =
227       MetricSample::CrashSample(name, /*num_samples=*/10);
228   EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename()));
229 
230   std::optional<int64_t> size = base::GetFileSize(filepath());
231   ASSERT_TRUE(size.has_value());
232   EXPECT_EQ(0, size.value());
233 }
234 
TEST_F(SerializationUtilsTest,ReadLongMessageTest)235 TEST_F(SerializationUtilsTest, ReadLongMessageTest) {
236   base::File test_file(filepath(),
237                        base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
238   std::string message(SerializationUtils::kMessageMaxLength + 1, 'c');
239 
240   int32_t message_size = message.length() + sizeof(int32_t);
241   test_file.WriteAtCurrentPos(base::byte_span_from_ref(message_size));
242   test_file.WriteAtCurrentPos(base::as_byte_span(message));
243   test_file.Close();
244 
245   std::unique_ptr<MetricSample> crash =
246       MetricSample::CrashSample("test", /*num_samples=*/10);
247   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
248 
249   std::vector<std::unique_ptr<MetricSample>> samples;
250   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &samples);
251   ASSERT_EQ(size_t(1), samples.size());
252   ASSERT_TRUE(samples[0].get() != nullptr);
253   EXPECT_TRUE(crash->IsEqual(*samples[0]));
254 }
255 
TEST_F(SerializationUtilsTest,NegativeLengthTest)256 TEST_F(SerializationUtilsTest, NegativeLengthTest) {
257   // This input is specifically constructed to yield a single crash sample when
258   // parsed by a buggy version of the code but fails to parse and doesn't yield
259   // samples when parsed by a correct implementation.
260   constexpr uint8_t kInput[] = {
261       // Length indicating that next length field is the negative one below.
262       // This sample is invalid as it contains more than three null bytes.
263       0x14,
264       0x00,
265       0x00,
266       0x00,
267       // Encoding of a valid crash sample.
268       0x0c,
269       0x00,
270       0x00,
271       0x00,
272       0x63,
273       0x72,
274       0x61,
275       0x73,
276       0x68,
277       0x00,
278       0x61,
279       0x00,
280       // Invalid sample that jumps past the negative length bytes below.
281       0x08,
282       0x00,
283       0x00,
284       0x00,
285       // This is -16 in two's complement interpretation, pointing to the valid
286       // crash sample before.
287       0xf0,
288       0xff,
289       0xff,
290       0xff,
291   };
292   ASSERT_TRUE(base::WriteFile(filepath(), base::span(kInput)));
293 
294   std::vector<std::unique_ptr<MetricSample>> samples;
295   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &samples);
296   ASSERT_EQ(0U, samples.size());
297 }
298 
TEST_F(SerializationUtilsTest,WriteReadTest_TruncateFile)299 TEST_F(SerializationUtilsTest, WriteReadTest_TruncateFile) {
300   std::unique_ptr<MetricSample> hist = MetricSample::HistogramSample(
301       "myhist", /*sample=*/1, /*min=*/2, /*max=*/3, /*bucket_count=*/4,
302       /*num_samples=*/5);
303   std::unique_ptr<MetricSample> crash =
304       MetricSample::CrashSample("mycrash", /*num_samples=*/10);
305   std::unique_ptr<MetricSample> lhist = MetricSample::LinearHistogramSample(
306       "linear", /*sample=*/1, /*max=*/10, /*num_samples=*/10);
307   std::unique_ptr<MetricSample> shist = MetricSample::SparseHistogramSample(
308       "mysparse", /*sample=*/30, /*num_samples=*/10);
309   std::unique_ptr<MetricSample> action =
310       MetricSample::UserActionSample("myaction", /*num_samples=*/1);
311 
312   SerializationUtils::WriteMetricToFile(*hist.get(), filename());
313   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
314   SerializationUtils::WriteMetricToFile(*lhist.get(), filename());
315   SerializationUtils::WriteMetricToFile(*shist.get(), filename());
316   SerializationUtils::WriteMetricToFile(*action.get(), filename());
317   std::vector<std::unique_ptr<MetricSample>> vect;
318   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
319   // NOTE: Should *not* have an entry for each repeated sample.
320   ASSERT_EQ(vect.size(), size_t(5));
321   for (auto& sample : vect) {
322     ASSERT_NE(nullptr, sample.get());
323   }
324   EXPECT_TRUE(hist->IsEqual(*vect[0]));
325   EXPECT_TRUE(crash->IsEqual(*vect[1]));
326   EXPECT_TRUE(lhist->IsEqual(*vect[2]));
327   EXPECT_TRUE(shist->IsEqual(*vect[3]));
328   EXPECT_TRUE(action->IsEqual(*vect[4]));
329 
330   std::optional<int64_t> size = base::GetFileSize(filepath());
331   ASSERT_TRUE(size.has_value());
332   ASSERT_EQ(0, size.value());
333 }
334 
TEST_F(SerializationUtilsTest,WriteReadTest_DeleteFile)335 TEST_F(SerializationUtilsTest, WriteReadTest_DeleteFile) {
336   std::unique_ptr<MetricSample> hist = MetricSample::HistogramSample(
337       "myhist", /*sample=*/1, /*min=*/2, /*max=*/3, /*bucket_count=*/4,
338       /*num_samples=*/5);
339   std::unique_ptr<MetricSample> crash =
340       MetricSample::CrashSample("mycrash", /*num_samples=*/10);
341   std::unique_ptr<MetricSample> lhist = MetricSample::LinearHistogramSample(
342       "linear", /*sample=*/1, /*max=*/10, /*num_samples=*/10);
343   std::unique_ptr<MetricSample> shist = MetricSample::SparseHistogramSample(
344       "mysparse", /*sample=*/30, /*num_samples=*/10);
345   std::unique_ptr<MetricSample> action =
346       MetricSample::UserActionSample("myaction", /*num_samples=*/1);
347 
348   SerializationUtils::WriteMetricToFile(*hist.get(), filename());
349   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
350   SerializationUtils::WriteMetricToFile(*lhist.get(), filename());
351   SerializationUtils::WriteMetricToFile(*shist.get(), filename());
352   SerializationUtils::WriteMetricToFile(*action.get(), filename());
353   std::vector<std::unique_ptr<MetricSample>> vect;
354   SerializationUtils::ReadAndDeleteMetricsFromFile(filename(), &vect);
355   // NOTE: Should *not* have an entry for each repeated sample.
356   ASSERT_EQ(vect.size(), size_t(5));
357   for (auto& sample : vect) {
358     ASSERT_NE(nullptr, sample.get());
359   }
360   EXPECT_TRUE(hist->IsEqual(*vect[0]));
361   EXPECT_TRUE(crash->IsEqual(*vect[1]));
362   EXPECT_TRUE(lhist->IsEqual(*vect[2]));
363   EXPECT_TRUE(shist->IsEqual(*vect[3]));
364   EXPECT_TRUE(action->IsEqual(*vect[4]));
365 
366   EXPECT_FALSE(base::PathExists(filepath()));
367 }
368 
TEST_F(SerializationUtilsTest,TooManyMessagesTest)369 TEST_F(SerializationUtilsTest, TooManyMessagesTest) {
370   std::unique_ptr<MetricSample> hist = MetricSample::HistogramSample(
371       "myhist", /*sample=*/1, /*min=*/2, /*max=*/3, /*bucket_count=*/4,
372       /*num_samples=*/5);
373 
374   constexpr int kDiscardedSamples = 50000;
375   for (int i = 0;
376        i < SerializationUtils::kMaxMessagesPerRead + kDiscardedSamples; i++) {
377     SerializationUtils::WriteMetricToFile(*hist.get(), filename());
378   }
379 
380   std::vector<std::unique_ptr<MetricSample>> vect;
381   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
382   ASSERT_EQ(SerializationUtils::kMaxMessagesPerRead,
383             static_cast<int>(vect.size()));
384   for (auto& sample : vect) {
385     ASSERT_NE(nullptr, sample.get());
386     EXPECT_TRUE(hist->IsEqual(*sample));
387   }
388 
389   std::optional<int64_t> size = base::GetFileSize(filepath());
390   ASSERT_TRUE(size.has_value());
391   ASSERT_EQ(0, size.value());
392 }
393 
TEST_F(SerializationUtilsTest,ReadEmptyFile)394 TEST_F(SerializationUtilsTest, ReadEmptyFile) {
395   {
396     // Create a zero-length file and then close file descriptor.
397     base::File file(filepath(),
398                     base::File::FLAG_CREATE | base::File::FLAG_WRITE);
399     ASSERT_TRUE(file.IsValid());
400   }
401 
402   std::vector<std::unique_ptr<MetricSample>> vect;
403   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
404   EXPECT_THAT(vect, IsEmpty());
405 }
406 
TEST_F(SerializationUtilsTest,ReadNonExistentFile)407 TEST_F(SerializationUtilsTest, ReadNonExistentFile) {
408   base::DeleteFile(filepath());  // Ensure non-existance.
409   base::HistogramTester histogram_tester;
410   std::vector<std::unique_ptr<MetricSample>> vect;
411   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
412   EXPECT_THAT(vect, IsEmpty());
413 }
414 
TEST_F(SerializationUtilsTest,ParseInvalidType)415 TEST_F(SerializationUtilsTest, ParseInvalidType) {
416   // Verify that parsing of an invalid sample type fails.
417   EXPECT_EQ(nullptr, SerializationUtils::ParseSample(base::StringPrintf(
418                          "not_a_type%cvalue%c", '\0', '\0')));
419 }
420 
421 }  // namespace
422 }  // namespace metrics
423