1 // Copyright 2016 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/file_metrics_provider.h"
6
7 #include <memory>
8
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/memory_mapped_file.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/metrics/histogram.h"
17 #include "base/metrics/histogram_base.h"
18 #include "base/metrics/histogram_flattener.h"
19 #include "base/metrics/histogram_snapshot_manager.h"
20 #include "base/metrics/persistent_histogram_allocator.h"
21 #include "base/metrics/persistent_memory_allocator.h"
22 #include "base/metrics/sparse_histogram.h"
23 #include "base/metrics/statistics_recorder.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/synchronization/waitable_event.h"
26 #include "base/test/bind.h"
27 #include "base/test/metrics/histogram_tester.h"
28 #include "base/test/task_environment.h"
29 #include "base/time/time.h"
30 #include "components/metrics/metrics_log.h"
31 #include "components/metrics/metrics_pref_names.h"
32 #include "components/metrics/persistent_system_profile.h"
33 #include "components/prefs/pref_registry_simple.h"
34 #include "components/prefs/testing_pref_service.h"
35 #include "testing/gmock/include/gmock/gmock.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37 #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
38 #include "third_party/metrics_proto/system_profile.pb.h"
39
40 namespace {
41 const char kMetricsName[] = "TestMetrics";
42 const char kMergedCountHistogramName[] =
43 "UMA.FileMetricsProvider.TestMetrics.MergedHistogramsCount";
44 const char kMetricsFilename[] = "file.metrics";
45
WriteSystemProfileToAllocator(base::PersistentHistogramAllocator * allocator)46 void WriteSystemProfileToAllocator(
47 base::PersistentHistogramAllocator* allocator) {
48 metrics::SystemProfileProto profile_proto;
49 // Add a field trial to verify that FileMetricsProvider will produce an
50 // independent log with the written system profile. Similarly for the session
51 // hash.
52 metrics::SystemProfileProto::FieldTrial* trial =
53 profile_proto.add_field_trial();
54 trial->set_name_id(123);
55 trial->set_group_id(456);
56 profile_proto.set_session_hash(789);
57 metrics::PersistentSystemProfile persistent_profile;
58 persistent_profile.RegisterPersistentAllocator(allocator->memory_allocator());
59 persistent_profile.SetSystemProfile(profile_proto, /*complete=*/true);
60 }
61 } // namespace
62
63 namespace metrics {
64
65 class HistogramFlattenerDeltaRecorder : public base::HistogramFlattener {
66 public:
67 HistogramFlattenerDeltaRecorder() = default;
68
69 HistogramFlattenerDeltaRecorder(const HistogramFlattenerDeltaRecorder&) =
70 delete;
71 HistogramFlattenerDeltaRecorder& operator=(
72 const HistogramFlattenerDeltaRecorder&) = delete;
73
RecordDelta(const base::HistogramBase & histogram,const base::HistogramSamples & snapshot)74 void RecordDelta(const base::HistogramBase& histogram,
75 const base::HistogramSamples& snapshot) override {
76 // Only remember locally created histograms; they have exactly 2 chars.
77 if (strlen(histogram.histogram_name()) == 2)
78 recorded_delta_histogram_names_.push_back(histogram.histogram_name());
79 }
80
GetRecordedDeltaHistogramNames()81 std::vector<std::string> GetRecordedDeltaHistogramNames() {
82 return recorded_delta_histogram_names_;
83 }
84
85 private:
86 std::vector<std::string> recorded_delta_histogram_names_;
87 };
88
89 // Exactly the same as FileMetricsProvider, but provides a way to "hook" into
90 // RecordSourcesChecked() and run a callback each time it is called so that it
91 // is easier to individually verify the sources being merged.
92 class TestFileMetricsProvider : public FileMetricsProvider {
93 public:
94 using FileMetricsProvider::FileMetricsProvider;
95
96 TestFileMetricsProvider(const TestFileMetricsProvider&) = delete;
97 TestFileMetricsProvider& operator=(const TestFileMetricsProvider&) = delete;
98
99 ~TestFileMetricsProvider() override = default;
100
101 // Sets the callback to run after RecordSourcesChecked() is called. Used to
102 // individually verify the sources being merged.
SetSourcesCheckedCallback(base::RepeatingClosure callback)103 void SetSourcesCheckedCallback(base::RepeatingClosure callback) {
104 callback_ = std::move(callback);
105 }
106
107 private:
108 // FileMetricsProvider:
RecordSourcesChecked(SourceInfoList * checked,std::vector<size_t> samples_counts)109 void RecordSourcesChecked(SourceInfoList* checked,
110 std::vector<size_t> samples_counts) override {
111 if (!callback_.is_null()) {
112 callback_.Run();
113 }
114
115 FileMetricsProvider::RecordSourcesChecked(checked, samples_counts);
116 }
117
118 // A callback to run after a call to RecordSourcesChecked().
119 base::RepeatingClosure callback_;
120 };
121
122 class FileMetricsProviderTest : public testing::TestWithParam<bool> {
123 public:
124 FileMetricsProviderTest(const FileMetricsProviderTest&) = delete;
125 FileMetricsProviderTest& operator=(const FileMetricsProviderTest&) = delete;
126
127 protected:
128 const size_t kSmallFileSize = 64 << 10; // 64 KiB
129 const size_t kLargeFileSize = 2 << 20; // 2 MiB
130
131 enum : int { kMaxCreateHistograms = 10 };
132
FileMetricsProviderTest()133 FileMetricsProviderTest()
134 : create_large_files_(GetParam()),
135 statistics_recorder_(
136 base::StatisticsRecorder::CreateTemporaryForTesting()),
137 prefs_(new TestingPrefServiceSimple) {
138 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
139 FileMetricsProvider::RegisterSourcePrefs(prefs_->registry(), kMetricsName);
140 }
141
~FileMetricsProviderTest()142 ~FileMetricsProviderTest() override {
143 // Clear out any final remaining tasks.
144 task_environment_.RunUntilIdle();
145 DCHECK_EQ(0U, filter_actions_remaining_);
146 // If a global histogram allocator exists at this point then it likely
147 // acquired histograms that will continue to point to the released
148 // memory and potentially cause use-after-free memory corruption.
149 DCHECK(!base::GlobalHistogramAllocator::Get());
150 }
151
task_environment()152 base::test::TaskEnvironment* task_environment() { return &task_environment_; }
prefs()153 TestingPrefServiceSimple* prefs() { return prefs_.get(); }
temp_dir()154 base::FilePath temp_dir() { return temp_dir_.GetPath(); }
metrics_file()155 base::FilePath metrics_file() {
156 return temp_dir_.GetPath().AppendASCII(kMetricsFilename);
157 }
158
provider()159 TestFileMetricsProvider* provider() {
160 if (!provider_)
161 provider_ = std::make_unique<TestFileMetricsProvider>(prefs());
162 return provider_.get();
163 }
164
OnDidCreateMetricsLog()165 void OnDidCreateMetricsLog() {
166 provider()->OnDidCreateMetricsLog();
167 }
168
HasPreviousSessionData()169 bool HasPreviousSessionData() { return provider()->HasPreviousSessionData(); }
170
MergeHistogramDeltas()171 void MergeHistogramDeltas() {
172 provider()->MergeHistogramDeltas(/*async=*/false,
173 /*done_callback=*/base::DoNothing());
174 }
175
HasIndependentMetrics()176 bool HasIndependentMetrics() { return provider()->HasIndependentMetrics(); }
177
ProvideIndependentMetrics(ChromeUserMetricsExtension * uma_proto,base::HistogramSnapshotManager * snapshot_manager)178 bool ProvideIndependentMetrics(
179 ChromeUserMetricsExtension* uma_proto,
180 base::HistogramSnapshotManager* snapshot_manager) {
181 bool success = false;
182 bool success_set = false;
183 provider()->ProvideIndependentMetrics(
184 base::DoNothing(),
185 base::BindOnce(
186 [](bool* success_ptr, bool* set_ptr, bool s) {
187 *success_ptr = s;
188 *set_ptr = true;
189 },
190 &success, &success_set),
191 uma_proto, snapshot_manager);
192
193 task_environment()->RunUntilIdle();
194 CHECK(success_set);
195 return success;
196 }
197
RecordInitialHistogramSnapshots(base::HistogramSnapshotManager * snapshot_manager)198 void RecordInitialHistogramSnapshots(
199 base::HistogramSnapshotManager* snapshot_manager) {
200 provider()->RecordInitialHistogramSnapshots(snapshot_manager);
201 }
202
GetSnapshotHistogramCount()203 size_t GetSnapshotHistogramCount() {
204 // Merge the data from the allocator into the StatisticsRecorder.
205 MergeHistogramDeltas();
206
207 // Flatten what is known to see what has changed since the last time.
208 HistogramFlattenerDeltaRecorder flattener;
209 base::HistogramSnapshotManager snapshot_manager(&flattener);
210 // "true" to the begin() includes histograms held in persistent storage.
211 base::StatisticsRecorder::PrepareDeltas(true, base::Histogram::kNoFlags,
212 base::Histogram::kNoFlags,
213 &snapshot_manager);
214 return flattener.GetRecordedDeltaHistogramNames().size();
215 }
216
GetIndependentHistogramCount()217 size_t GetIndependentHistogramCount() {
218 HistogramFlattenerDeltaRecorder flattener;
219 base::HistogramSnapshotManager snapshot_manager(&flattener);
220 ChromeUserMetricsExtension uma_proto;
221 provider()->ProvideIndependentMetrics(base::DoNothing(),
222 base::BindOnce([](bool success) {}),
223 &uma_proto, &snapshot_manager);
224
225 task_environment()->RunUntilIdle();
226 return flattener.GetRecordedDeltaHistogramNames().size();
227 }
228
CreateGlobalHistograms(int histogram_count)229 void CreateGlobalHistograms(int histogram_count) {
230 DCHECK_GT(kMaxCreateHistograms, histogram_count);
231
232 // Create both sparse and normal histograms in the allocator. Make them
233 // stability histograms to ensure that the histograms are snapshotted (in
234 // the case of stability logs) or are put into independent logs. Histogram
235 // names must be 2 characters (see HistogramFlattenerDeltaRecorder).
236 created_histograms_[0] = base::SparseHistogram::FactoryGet(
237 "h0", /*flags=*/base::HistogramBase::Flags::kUmaStabilityHistogramFlag);
238 created_histograms_[0]->Add(0);
239 for (int i = 1; i < histogram_count; ++i) {
240 created_histograms_[i] = base::Histogram::FactoryGet(
241 base::StringPrintf("h%d", i), 1, 100, 10,
242 /*flags=*/base::HistogramBase::Flags::kUmaStabilityHistogramFlag);
243 created_histograms_[i]->Add(i);
244 }
245 }
246
WriteMetricsFile(const base::FilePath & path,base::PersistentHistogramAllocator * metrics)247 void WriteMetricsFile(const base::FilePath& path,
248 base::PersistentHistogramAllocator* metrics) {
249 base::File writer(path,
250 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
251 // Use DCHECK so the stack-trace will indicate where this was called.
252 DCHECK(writer.IsValid()) << path;
253 size_t file_size = create_large_files_ ? metrics->size() : metrics->used();
254 int written = writer.Write(0, (const char*)metrics->data(), file_size);
255 DCHECK_EQ(static_cast<int>(file_size), written);
256 }
257
WriteMetricsFileAtTime(const base::FilePath & path,base::PersistentHistogramAllocator * metrics,base::Time write_time)258 void WriteMetricsFileAtTime(const base::FilePath& path,
259 base::PersistentHistogramAllocator* metrics,
260 base::Time write_time) {
261 WriteMetricsFile(path, metrics);
262 base::TouchFile(path, write_time, write_time);
263 }
264
CreateMetricsFileWithHistograms(const base::FilePath & file_path,base::Time write_time,int histogram_count,base::OnceCallback<void (base::PersistentHistogramAllocator *)> callback)265 base::GlobalHistogramAllocator* CreateMetricsFileWithHistograms(
266 const base::FilePath& file_path,
267 base::Time write_time,
268 int histogram_count,
269 base::OnceCallback<void(base::PersistentHistogramAllocator*)> callback) {
270 base::GlobalHistogramAllocator::CreateWithLocalMemory(
271 create_large_files_ ? kLargeFileSize : kSmallFileSize,
272 0, kMetricsName);
273
274 CreateGlobalHistograms(histogram_count);
275
276 base::GlobalHistogramAllocator* histogram_allocator =
277 base::GlobalHistogramAllocator::ReleaseForTesting();
278 std::move(callback).Run(histogram_allocator);
279
280 WriteMetricsFileAtTime(file_path, histogram_allocator, write_time);
281 return histogram_allocator;
282 }
283
CreateMetricsFileWithHistograms(int histogram_count)284 base::GlobalHistogramAllocator* CreateMetricsFileWithHistograms(
285 int histogram_count) {
286 return CreateMetricsFileWithHistograms(
287 metrics_file(), base::Time::Now(), histogram_count,
288 base::BindOnce([](base::PersistentHistogramAllocator* allocator) {}));
289 }
290
GetCreatedHistogram(int index)291 base::HistogramBase* GetCreatedHistogram(int index) {
292 DCHECK_GT(kMaxCreateHistograms, index);
293 return created_histograms_[index];
294 }
295
SetFilterActions(FileMetricsProvider::Params * params,const FileMetricsProvider::FilterAction * actions,size_t count)296 void SetFilterActions(FileMetricsProvider::Params* params,
297 const FileMetricsProvider::FilterAction* actions,
298 size_t count) {
299 filter_actions_ = actions;
300 filter_actions_remaining_ = count;
301 params->filter = base::BindRepeating(
302 &FileMetricsProviderTest::FilterSourcePath, base::Unretained(this));
303 }
304
305 const bool create_large_files_;
306
307 private:
FilterSourcePath(const base::FilePath & path)308 FileMetricsProvider::FilterAction FilterSourcePath(
309 const base::FilePath& path) {
310 DCHECK_LT(0U, filter_actions_remaining_);
311 --filter_actions_remaining_;
312 return *filter_actions_++;
313 }
314
315 base::test::TaskEnvironment task_environment_;
316 std::unique_ptr<base::StatisticsRecorder> statistics_recorder_;
317 base::ScopedTempDir temp_dir_;
318 std::unique_ptr<TestingPrefServiceSimple> prefs_;
319 std::unique_ptr<TestFileMetricsProvider> provider_;
320 base::HistogramBase* created_histograms_[kMaxCreateHistograms];
321
322 raw_ptr<const FileMetricsProvider::FilterAction, AllowPtrArithmetic>
323 filter_actions_ = nullptr;
324 size_t filter_actions_remaining_ = 0;
325 };
326
327 // Run all test cases with both small and large files.
328 INSTANTIATE_TEST_SUITE_P(SmallAndLargeFiles,
329 FileMetricsProviderTest,
330 testing::Bool());
331
TEST_P(FileMetricsProviderTest,AccessMetrics)332 TEST_P(FileMetricsProviderTest, AccessMetrics) {
333 ASSERT_FALSE(PathExists(metrics_file()));
334 base::HistogramTester histogram_tester;
335
336 base::Time metrics_time = base::Time::Now() - base::Minutes(5);
337 base::GlobalHistogramAllocator* histogram_allocator =
338 CreateMetricsFileWithHistograms(2);
339 ASSERT_TRUE(PathExists(metrics_file()));
340 base::TouchFile(metrics_file(), metrics_time, metrics_time);
341
342 // Register the file and allow the "checker" task to run.
343 provider()->RegisterSource(FileMetricsProvider::Params(
344 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
345 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName));
346 histogram_tester.ExpectTotalCount(kMergedCountHistogramName,
347 /*expected_count=*/0);
348
349 // Record embedded snapshots via snapshot-manager.
350 OnDidCreateMetricsLog();
351 task_environment()->RunUntilIdle();
352 EXPECT_EQ(2U, GetSnapshotHistogramCount());
353 histogram_tester.ExpectUniqueSample(kMergedCountHistogramName, /*sample=*/2,
354 /*expected_bucket_count=*/1);
355 EXPECT_FALSE(base::PathExists(metrics_file()));
356
357 // Make sure a second call to the snapshot-recorder doesn't break anything.
358 OnDidCreateMetricsLog();
359 task_environment()->RunUntilIdle();
360 EXPECT_EQ(0U, GetSnapshotHistogramCount());
361
362 // File should have been deleted but recreate it to test behavior should
363 // the file not be deletable by this process.
364 WriteMetricsFileAtTime(metrics_file(), histogram_allocator, metrics_time);
365
366 // Second full run on the same file should produce nothing.
367 OnDidCreateMetricsLog();
368 task_environment()->RunUntilIdle();
369 EXPECT_EQ(0U, GetSnapshotHistogramCount());
370 histogram_tester.ExpectUniqueSample(kMergedCountHistogramName, /*sample=*/2,
371 /*expected_bucket_count=*/1);
372 EXPECT_FALSE(base::PathExists(metrics_file()));
373
374 // Recreate the file to indicate that it is "new" and must be recorded.
375 metrics_time = metrics_time + base::Minutes(1);
376 WriteMetricsFileAtTime(metrics_file(), histogram_allocator, metrics_time);
377
378 // This run should again have "new" histograms.
379 OnDidCreateMetricsLog();
380 task_environment()->RunUntilIdle();
381 EXPECT_EQ(2U, GetSnapshotHistogramCount());
382 histogram_tester.ExpectUniqueSample(kMergedCountHistogramName, /*sample=*/2,
383 /*expected_bucket_count=*/2);
384 EXPECT_FALSE(base::PathExists(metrics_file()));
385 }
386
TEST_P(FileMetricsProviderTest,AccessTimeLimitedFile)387 TEST_P(FileMetricsProviderTest, AccessTimeLimitedFile) {
388 ASSERT_FALSE(PathExists(metrics_file()));
389
390 base::Time metrics_time = base::Time::Now() - base::Hours(5);
391 CreateMetricsFileWithHistograms(2);
392 ASSERT_TRUE(PathExists(metrics_file()));
393 base::TouchFile(metrics_file(), metrics_time, metrics_time);
394
395 // Register the file and allow the "checker" task to run.
396 FileMetricsProvider::Params params(
397 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
398 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName);
399 params.max_age = base::Hours(1);
400 provider()->RegisterSource(params);
401
402 // Attempt to access the file should return nothing.
403 OnDidCreateMetricsLog();
404 task_environment()->RunUntilIdle();
405 EXPECT_EQ(0U, GetSnapshotHistogramCount());
406 EXPECT_FALSE(base::PathExists(metrics_file()));
407 }
408
TEST_P(FileMetricsProviderTest,FilterDelaysFile)409 TEST_P(FileMetricsProviderTest, FilterDelaysFile) {
410 ASSERT_FALSE(PathExists(metrics_file()));
411
412 base::Time now_time = base::Time::Now();
413 base::Time metrics_time = now_time - base::Minutes(5);
414 CreateMetricsFileWithHistograms(2);
415 ASSERT_TRUE(PathExists(metrics_file()));
416 base::TouchFile(metrics_file(), metrics_time, metrics_time);
417 base::File::Info fileinfo;
418 ASSERT_TRUE(base::GetFileInfo(metrics_file(), &fileinfo));
419 EXPECT_GT(base::Time::Now(), fileinfo.last_modified);
420
421 // Register the file and allow the "checker" task to run.
422 FileMetricsProvider::Params params(
423 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
424 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName);
425 const FileMetricsProvider::FilterAction actions[] = {
426 FileMetricsProvider::FILTER_TRY_LATER,
427 FileMetricsProvider::FILTER_PROCESS_FILE};
428 SetFilterActions(¶ms, actions, std::size(actions));
429 provider()->RegisterSource(params);
430
431 // Processing the file should touch it but yield no results. File timestamp
432 // accuracy is limited so compare the touched time to a couple seconds past.
433 OnDidCreateMetricsLog();
434 task_environment()->RunUntilIdle();
435 EXPECT_EQ(0U, GetSnapshotHistogramCount());
436 EXPECT_TRUE(base::PathExists(metrics_file()));
437 ASSERT_TRUE(base::GetFileInfo(metrics_file(), &fileinfo));
438 EXPECT_LT(metrics_time, fileinfo.last_modified);
439 EXPECT_LE(now_time - base::Seconds(2), fileinfo.last_modified);
440
441 // Second full run on the same file should process the file.
442 OnDidCreateMetricsLog();
443 task_environment()->RunUntilIdle();
444 EXPECT_EQ(2U, GetSnapshotHistogramCount());
445 EXPECT_FALSE(base::PathExists(metrics_file()));
446 }
447
TEST_P(FileMetricsProviderTest,FilterSkipsFile)448 TEST_P(FileMetricsProviderTest, FilterSkipsFile) {
449 ASSERT_FALSE(PathExists(metrics_file()));
450
451 base::Time now_time = base::Time::Now();
452 base::Time metrics_time = now_time - base::Minutes(5);
453 CreateMetricsFileWithHistograms(2);
454 ASSERT_TRUE(PathExists(metrics_file()));
455 base::TouchFile(metrics_file(), metrics_time, metrics_time);
456 base::File::Info fileinfo;
457 ASSERT_TRUE(base::GetFileInfo(metrics_file(), &fileinfo));
458 EXPECT_GT(base::Time::Now(), fileinfo.last_modified);
459
460 // Register the file and allow the "checker" task to run.
461 FileMetricsProvider::Params params(
462 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
463 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName);
464 const FileMetricsProvider::FilterAction actions[] = {
465 FileMetricsProvider::FILTER_SKIP_FILE};
466 SetFilterActions(¶ms, actions, std::size(actions));
467 provider()->RegisterSource(params);
468
469 // Processing the file should delete it.
470 OnDidCreateMetricsLog();
471 task_environment()->RunUntilIdle();
472 EXPECT_EQ(0U, GetSnapshotHistogramCount());
473 EXPECT_FALSE(base::PathExists(metrics_file()));
474 }
475
TEST_P(FileMetricsProviderTest,AccessDirectory)476 TEST_P(FileMetricsProviderTest, AccessDirectory) {
477 ASSERT_FALSE(PathExists(metrics_file()));
478
479 base::GlobalHistogramAllocator::CreateWithLocalMemory(
480 64 << 10, 0, kMetricsName);
481 base::GlobalHistogramAllocator* allocator =
482 base::GlobalHistogramAllocator::Get();
483 base::HistogramBase* histogram;
484
485 // Create files starting with a timestamp a few minutes back.
486 base::Time base_time = base::Time::Now() - base::Minutes(10);
487
488 // Create some files in an odd order. The files are "touched" back in time to
489 // ensure that each file has a later timestamp on disk than the previous one.
490 base::ScopedTempDir metrics_files;
491 EXPECT_TRUE(metrics_files.CreateUniqueTempDir());
492 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII(".foo.pma"),
493 allocator, base_time);
494 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("_bar.pma"),
495 allocator, base_time);
496 // Histogram names must be 2 characters (see HistogramFlattenerDeltaRecorder).
497 histogram = base::Histogram::FactoryGet("h1", 1, 100, 10, 0);
498 histogram->Add(1);
499 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("a1.pma"),
500 allocator, base_time + base::Minutes(1));
501
502 histogram = base::Histogram::FactoryGet("h2", 1, 100, 10, 0);
503 histogram->Add(2);
504 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("c2.pma"),
505 allocator, base_time + base::Minutes(2));
506
507 histogram = base::Histogram::FactoryGet("h3", 1, 100, 10, 0);
508 histogram->Add(3);
509 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("b3.pma"),
510 allocator, base_time + base::Minutes(3));
511
512 histogram = base::Histogram::FactoryGet("h4", 1, 100, 10, 0);
513 histogram->Add(3);
514 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("d4.pma"),
515 allocator, base_time + base::Minutes(4));
516
517 base::TouchFile(metrics_files.GetPath().AppendASCII("b3.pma"),
518 base_time + base::Minutes(5), base_time + base::Minutes(5));
519
520 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("baz"), allocator,
521 base_time + base::Minutes(6));
522
523 // The global allocator has to be detached here so that no metrics created
524 // by code called below get stored in it as that would make for potential
525 // use-after-free operations if that code is called again.
526 base::GlobalHistogramAllocator::ReleaseForTesting();
527
528 // Register the file and allow the "checker" task to run.
529 provider()->RegisterSource(FileMetricsProvider::Params(
530 metrics_files.GetPath(),
531 FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
532 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName));
533
534 // Record embedded snapshots via snapshot-manager.
535 std::vector<uint32_t> actual_order;
536 provider()->SetSourcesCheckedCallback(base::BindLambdaForTesting(
537 [&] { actual_order.push_back(GetSnapshotHistogramCount()); }));
538 OnDidCreateMetricsLog();
539 task_environment()->RunUntilIdle();
540
541 // Files could come out in the order: a1, c2, d4, b3. They are recognizable by
542 // the number of histograms contained within each. The "0" is the last merge
543 // done, which detects that there are no more files to merge.
544 EXPECT_THAT(actual_order, testing::ElementsAre(1, 2, 4, 3, 0));
545
546 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("a1.pma")));
547 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("c2.pma")));
548 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("b3.pma")));
549 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("d4.pma")));
550 EXPECT_TRUE(
551 base::PathExists(metrics_files.GetPath().AppendASCII(".foo.pma")));
552 EXPECT_TRUE(
553 base::PathExists(metrics_files.GetPath().AppendASCII("_bar.pma")));
554 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("baz")));
555 }
556
TEST_P(FileMetricsProviderTest,AccessDirectoryWithInvalidFiles)557 TEST_P(FileMetricsProviderTest, AccessDirectoryWithInvalidFiles) {
558 ASSERT_FALSE(PathExists(metrics_file()));
559
560 // Create files starting with a timestamp a few minutes back.
561 base::Time base_time = base::Time::Now() - base::Minutes(10);
562
563 base::ScopedTempDir metrics_files;
564 EXPECT_TRUE(metrics_files.CreateUniqueTempDir());
565
566 CreateMetricsFileWithHistograms(
567 metrics_files.GetPath().AppendASCII("h1.pma"),
568 base_time + base::Minutes(1), 1,
569 base::BindOnce([](base::PersistentHistogramAllocator* allocator) {
570 allocator->memory_allocator()->SetMemoryState(
571 base::PersistentMemoryAllocator::MEMORY_DELETED);
572 }));
573
574 CreateMetricsFileWithHistograms(
575 metrics_files.GetPath().AppendASCII("h2.pma"),
576 base_time + base::Minutes(2), 2,
577 base::BindOnce(&WriteSystemProfileToAllocator));
578
579 CreateMetricsFileWithHistograms(
580 metrics_files.GetPath().AppendASCII("h3.pma"),
581 base_time + base::Minutes(3), 3,
582 base::BindOnce([](base::PersistentHistogramAllocator* allocator) {
583 allocator->memory_allocator()->SetMemoryState(
584 base::PersistentMemoryAllocator::MEMORY_DELETED);
585 }));
586
587 {
588 base::File empty(metrics_files.GetPath().AppendASCII("h4.pma"),
589 base::File::FLAG_CREATE | base::File::FLAG_WRITE);
590 }
591 base::TouchFile(metrics_files.GetPath().AppendASCII("h4.pma"),
592 base_time + base::Minutes(4), base_time + base::Minutes(4));
593
594 // Register the file and allow the "checker" task to run.
595 provider()->RegisterSource(FileMetricsProvider::Params(
596 metrics_files.GetPath(),
597 FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
598 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName));
599
600 // No files yet.
601 EXPECT_EQ(0U, GetIndependentHistogramCount());
602 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h1.pma")));
603 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma")));
604 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma")));
605 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma")));
606
607 // H1 should be skipped and H2 available.
608 OnDidCreateMetricsLog();
609 task_environment()->RunUntilIdle();
610 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h1.pma")));
611 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma")));
612 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma")));
613 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma")));
614
615 // H2 should be read and the file deleted.
616 EXPECT_EQ(2U, GetIndependentHistogramCount());
617 task_environment()->RunUntilIdle();
618 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h2.pma")));
619
620 // Nothing else should be found but the last (valid but empty) file will
621 // stick around to be processed later (should it get expanded).
622 EXPECT_EQ(0U, GetIndependentHistogramCount());
623 task_environment()->RunUntilIdle();
624 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("h3.pma")));
625 EXPECT_TRUE(base::PathExists(metrics_files.GetPath().AppendASCII("h4.pma")));
626 }
627
TEST_P(FileMetricsProviderTest,AccessTimeLimitedDirectory)628 TEST_P(FileMetricsProviderTest, AccessTimeLimitedDirectory) {
629 ASSERT_FALSE(PathExists(metrics_file()));
630
631 base::GlobalHistogramAllocator::CreateWithLocalMemory(64 << 10, 0,
632 kMetricsName);
633 base::GlobalHistogramAllocator* allocator =
634 base::GlobalHistogramAllocator::Get();
635 base::HistogramBase* histogram;
636
637 // Create one old file and one new file. Histogram names must be 2 characters
638 // (see HistogramFlattenerDeltaRecorder).
639 base::ScopedTempDir metrics_files;
640 EXPECT_TRUE(metrics_files.CreateUniqueTempDir());
641 histogram = base::Histogram::FactoryGet("h1", 1, 100, 10, 0);
642 histogram->Add(1);
643 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("a1.pma"),
644 allocator, base::Time::Now() - base::Hours(1));
645
646 histogram = base::Histogram::FactoryGet("h2", 1, 100, 10, 0);
647 histogram->Add(2);
648 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("b2.pma"),
649 allocator, base::Time::Now());
650
651 // The global allocator has to be detached here so that no metrics created
652 // by code called below get stored in it as that would make for potential
653 // use-after-free operations if that code is called again.
654 base::GlobalHistogramAllocator::ReleaseForTesting();
655
656 // Register the file and allow the "checker" task to run.
657 FileMetricsProvider::Params params(
658 metrics_files.GetPath(),
659 FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
660 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName);
661 params.max_age = base::Minutes(30);
662 provider()->RegisterSource(params);
663
664 // Only b2, with 2 histograms, should be read.
665 OnDidCreateMetricsLog();
666 task_environment()->RunUntilIdle();
667 EXPECT_EQ(2U, GetSnapshotHistogramCount());
668 OnDidCreateMetricsLog();
669 task_environment()->RunUntilIdle();
670 EXPECT_EQ(0U, GetSnapshotHistogramCount());
671
672 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("a1.pma")));
673 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("b2.pma")));
674 }
675
TEST_P(FileMetricsProviderTest,AccessCountLimitedDirectory)676 TEST_P(FileMetricsProviderTest, AccessCountLimitedDirectory) {
677 ASSERT_FALSE(PathExists(metrics_file()));
678
679 base::GlobalHistogramAllocator::CreateWithLocalMemory(64 << 10, 0,
680 kMetricsName);
681 base::GlobalHistogramAllocator* allocator =
682 base::GlobalHistogramAllocator::Get();
683 base::HistogramBase* histogram;
684
685 // Create one old file and one new file. Histogram names must be 2 characters
686 // (see HistogramFlattenerDeltaRecorder).
687 base::ScopedTempDir metrics_files;
688 EXPECT_TRUE(metrics_files.CreateUniqueTempDir());
689 histogram = base::Histogram::FactoryGet("h1", 1, 100, 10, 0);
690 histogram->Add(1);
691 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("a1.pma"),
692 allocator, base::Time::Now() - base::Hours(1));
693
694 histogram = base::Histogram::FactoryGet("h2", 1, 100, 10, 0);
695 histogram->Add(2);
696 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("b2.pma"),
697 allocator, base::Time::Now());
698
699 // The global allocator has to be detached here so that no metrics created
700 // by code called below get stored in it as that would make for potential
701 // use-after-free operations if that code is called again.
702 base::GlobalHistogramAllocator::ReleaseForTesting();
703
704 // Register the file and allow the "checker" task to run.
705 FileMetricsProvider::Params params(
706 metrics_files.GetPath(),
707 FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
708 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName);
709 params.max_dir_files = 1;
710 provider()->RegisterSource(params);
711
712 // Only b2, with 2 histograms, should be read.
713 OnDidCreateMetricsLog();
714 task_environment()->RunUntilIdle();
715 EXPECT_EQ(2U, GetSnapshotHistogramCount());
716 OnDidCreateMetricsLog();
717 task_environment()->RunUntilIdle();
718 EXPECT_EQ(0U, GetSnapshotHistogramCount());
719
720 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("a1.pma")));
721 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("b2.pma")));
722 }
723
TEST_P(FileMetricsProviderTest,AccessSizeLimitedDirectory)724 TEST_P(FileMetricsProviderTest, AccessSizeLimitedDirectory) {
725 // This only works with large files that are big enough to count.
726 if (!create_large_files_)
727 return;
728
729 ASSERT_FALSE(PathExists(metrics_file()));
730
731 size_t file_size_kib = 64;
732 base::GlobalHistogramAllocator::CreateWithLocalMemory(file_size_kib << 10, 0,
733 kMetricsName);
734 base::GlobalHistogramAllocator* allocator =
735 base::GlobalHistogramAllocator::Get();
736 base::HistogramBase* histogram;
737
738 // Create one old file and one new file. Histogram names must be 2 characters
739 // (see HistogramFlattenerDeltaRecorder).
740 base::ScopedTempDir metrics_files;
741 EXPECT_TRUE(metrics_files.CreateUniqueTempDir());
742 histogram = base::Histogram::FactoryGet("h1", 1, 100, 10, 0);
743 histogram->Add(1);
744 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("a1.pma"),
745 allocator, base::Time::Now() - base::Hours(1));
746
747 histogram = base::Histogram::FactoryGet("h2", 1, 100, 10, 0);
748 histogram->Add(2);
749 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("b2.pma"),
750 allocator, base::Time::Now());
751
752 // The global allocator has to be detached here so that no metrics created
753 // by code called below get stored in it as that would make for potential
754 // use-after-free operations if that code is called again.
755 base::GlobalHistogramAllocator::ReleaseForTesting();
756
757 // Register the file and allow the "checker" task to run.
758 FileMetricsProvider::Params params(
759 metrics_files.GetPath(),
760 FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
761 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName);
762 params.max_dir_kib = file_size_kib + 1;
763 provider()->RegisterSource(params);
764
765 // Only b2, with 2 histograms, should be read.
766 OnDidCreateMetricsLog();
767 task_environment()->RunUntilIdle();
768 EXPECT_EQ(2U, GetSnapshotHistogramCount());
769 OnDidCreateMetricsLog();
770 task_environment()->RunUntilIdle();
771 EXPECT_EQ(0U, GetSnapshotHistogramCount());
772
773 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("a1.pma")));
774 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("b2.pma")));
775 }
776
TEST_P(FileMetricsProviderTest,AccessFilteredDirectory)777 TEST_P(FileMetricsProviderTest, AccessFilteredDirectory) {
778 ASSERT_FALSE(PathExists(metrics_file()));
779
780 base::GlobalHistogramAllocator::CreateWithLocalMemory(64 << 10, 0,
781 kMetricsName);
782 base::GlobalHistogramAllocator* allocator =
783 base::GlobalHistogramAllocator::Get();
784 base::HistogramBase* histogram;
785
786 // Create files starting with a timestamp a few minutes back.
787 base::Time base_time = base::Time::Now() - base::Minutes(10);
788
789 // Create some files in an odd order. The files are "touched" back in time to
790 // ensure that each file has a later timestamp on disk than the previous one.
791 base::ScopedTempDir metrics_files;
792 EXPECT_TRUE(metrics_files.CreateUniqueTempDir());
793 // Histogram names must be 2 characters (see HistogramFlattenerDeltaRecorder).
794 histogram = base::Histogram::FactoryGet("h1", 1, 100, 10, 0);
795 histogram->Add(1);
796 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("a1.pma"),
797 allocator, base_time + base::Minutes(1));
798
799 histogram = base::Histogram::FactoryGet("h2", 1, 100, 10, 0);
800 histogram->Add(2);
801 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("c2.pma"),
802 allocator, base_time + base::Minutes(2));
803
804 histogram = base::Histogram::FactoryGet("h3", 1, 100, 10, 0);
805 histogram->Add(3);
806 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("b3.pma"),
807 allocator, base_time + base::Minutes(3));
808
809 histogram = base::Histogram::FactoryGet("h4", 1, 100, 10, 0);
810 histogram->Add(3);
811 WriteMetricsFileAtTime(metrics_files.GetPath().AppendASCII("d4.pma"),
812 allocator, base_time + base::Minutes(4));
813
814 base::TouchFile(metrics_files.GetPath().AppendASCII("b3.pma"),
815 base_time + base::Minutes(5), base_time + base::Minutes(5));
816
817 // The global allocator has to be detached here so that no metrics created
818 // by code called below get stored in it as that would make for potential
819 // use-after-free operations if that code is called again.
820 base::GlobalHistogramAllocator::ReleaseForTesting();
821
822 // Register the file and allow the "checker" task to run.
823 FileMetricsProvider::Params params(
824 metrics_files.GetPath(),
825 FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
826 FileMetricsProvider::ASSOCIATE_CURRENT_RUN, kMetricsName);
827 const FileMetricsProvider::FilterAction actions[] = {
828 FileMetricsProvider::FILTER_PROCESS_FILE, // a1
829 FileMetricsProvider::FILTER_TRY_LATER, // c2
830 FileMetricsProvider::FILTER_SKIP_FILE, // d4
831 FileMetricsProvider::FILTER_PROCESS_FILE, // b3
832 FileMetricsProvider::FILTER_PROCESS_FILE}; // c2 (again)
833 SetFilterActions(¶ms, actions, std::size(actions));
834 provider()->RegisterSource(params);
835
836 // Record embedded snapshots via snapshot-manager.
837 std::vector<uint32_t> actual_order;
838 provider()->SetSourcesCheckedCallback(base::BindLambdaForTesting(
839 [&] { actual_order.push_back(GetSnapshotHistogramCount()); }));
840 OnDidCreateMetricsLog();
841 task_environment()->RunUntilIdle();
842
843 // Files could come out in the order: a1, b3, c2. They are recognizable by the
844 // number of histograms contained within each. The "0" is the last merge done,
845 // which detects that there are no more files to merge.
846 EXPECT_THAT(actual_order, testing::ElementsAre(1, 3, 2, 0));
847
848 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("a1.pma")));
849 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("c2.pma")));
850 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("b3.pma")));
851 EXPECT_FALSE(base::PathExists(metrics_files.GetPath().AppendASCII("d4.pma")));
852 }
853
TEST_P(FileMetricsProviderTest,AccessReadWriteMetrics)854 TEST_P(FileMetricsProviderTest, AccessReadWriteMetrics) {
855 // Create a global histogram allocator that maps to a file.
856 ASSERT_FALSE(PathExists(metrics_file()));
857 base::GlobalHistogramAllocator::CreateWithFile(
858 metrics_file(),
859 create_large_files_ ? kLargeFileSize : kSmallFileSize,
860 0, kMetricsName);
861 CreateGlobalHistograms(2);
862 ASSERT_TRUE(PathExists(metrics_file()));
863 base::HistogramBase* h0 = GetCreatedHistogram(0);
864 base::HistogramBase* h1 = GetCreatedHistogram(1);
865 DCHECK(h0);
866 DCHECK(h1);
867 base::GlobalHistogramAllocator::ReleaseForTesting();
868
869 // Register the file and allow the "checker" task to run.
870 provider()->RegisterSource(FileMetricsProvider::Params(
871 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ACTIVE_FILE,
872 FileMetricsProvider::ASSOCIATE_CURRENT_RUN));
873
874 // Record embedded snapshots via snapshot-manager.
875 OnDidCreateMetricsLog();
876 task_environment()->RunUntilIdle();
877 EXPECT_EQ(2U, GetSnapshotHistogramCount());
878 EXPECT_TRUE(base::PathExists(metrics_file()));
879
880 // Make sure a second call to the snapshot-recorder doesn't break anything.
881 OnDidCreateMetricsLog();
882 task_environment()->RunUntilIdle();
883 EXPECT_EQ(0U, GetSnapshotHistogramCount());
884 EXPECT_TRUE(base::PathExists(metrics_file()));
885
886 // Change a histogram and ensure that it's counted.
887 h0->Add(0);
888 EXPECT_EQ(1U, GetSnapshotHistogramCount());
889 EXPECT_TRUE(base::PathExists(metrics_file()));
890
891 // Change the other histogram and verify.
892 h1->Add(11);
893 EXPECT_EQ(1U, GetSnapshotHistogramCount());
894 EXPECT_TRUE(base::PathExists(metrics_file()));
895 }
896
TEST_P(FileMetricsProviderTest,AccessInitialMetrics)897 TEST_P(FileMetricsProviderTest, AccessInitialMetrics) {
898 ASSERT_FALSE(PathExists(metrics_file()));
899 CreateMetricsFileWithHistograms(2);
900
901 // Register the file and allow the "checker" task to run.
902 ASSERT_TRUE(PathExists(metrics_file()));
903 provider()->RegisterSource(FileMetricsProvider::Params(
904 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
905 FileMetricsProvider::ASSOCIATE_PREVIOUS_RUN, kMetricsName));
906
907 // Record embedded snapshots via snapshot-manager.
908 ASSERT_TRUE(HasPreviousSessionData());
909 task_environment()->RunUntilIdle();
910 {
911 HistogramFlattenerDeltaRecorder flattener;
912 base::HistogramSnapshotManager snapshot_manager(&flattener);
913 RecordInitialHistogramSnapshots(&snapshot_manager);
914 EXPECT_EQ(2U, flattener.GetRecordedDeltaHistogramNames().size());
915 }
916 EXPECT_TRUE(base::PathExists(metrics_file()));
917 OnDidCreateMetricsLog();
918 task_environment()->RunUntilIdle();
919 EXPECT_FALSE(base::PathExists(metrics_file()));
920
921 // A run for normal histograms should produce nothing.
922 CreateMetricsFileWithHistograms(2);
923 OnDidCreateMetricsLog();
924 task_environment()->RunUntilIdle();
925 EXPECT_EQ(0U, GetSnapshotHistogramCount());
926 EXPECT_TRUE(base::PathExists(metrics_file()));
927 OnDidCreateMetricsLog();
928 task_environment()->RunUntilIdle();
929 EXPECT_TRUE(base::PathExists(metrics_file()));
930 }
931
TEST_P(FileMetricsProviderTest,AccessEmbeddedProfileMetricsWithoutProfile)932 TEST_P(FileMetricsProviderTest, AccessEmbeddedProfileMetricsWithoutProfile) {
933 ASSERT_FALSE(PathExists(metrics_file()));
934 CreateMetricsFileWithHistograms(2);
935
936 // Register the file and allow the "checker" task to run.
937 ASSERT_TRUE(PathExists(metrics_file()));
938 provider()->RegisterSource(FileMetricsProvider::Params(
939 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
940 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName));
941
942 // Record embedded snapshots via snapshot-manager.
943 OnDidCreateMetricsLog();
944 task_environment()->RunUntilIdle();
945 {
946 HistogramFlattenerDeltaRecorder flattener;
947 base::HistogramSnapshotManager snapshot_manager(&flattener);
948 ChromeUserMetricsExtension uma_proto;
949
950 // A read of metrics with internal profiles should return nothing.
951 EXPECT_FALSE(HasIndependentMetrics());
952 EXPECT_FALSE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
953 }
954 EXPECT_TRUE(base::PathExists(metrics_file()));
955 OnDidCreateMetricsLog();
956 task_environment()->RunUntilIdle();
957 EXPECT_FALSE(base::PathExists(metrics_file()));
958 }
959
TEST_P(FileMetricsProviderTest,AccessEmbeddedProfileMetricsWithProfile)960 TEST_P(FileMetricsProviderTest, AccessEmbeddedProfileMetricsWithProfile) {
961 ASSERT_FALSE(PathExists(metrics_file()));
962 CreateMetricsFileWithHistograms(
963 metrics_file(), base::Time::Now(), 2,
964 base::BindOnce(&WriteSystemProfileToAllocator));
965
966 // Register the file and allow the "checker" task to run.
967 ASSERT_TRUE(PathExists(metrics_file()));
968 provider()->RegisterSource(FileMetricsProvider::Params(
969 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
970 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName));
971
972 // Record embedded snapshots via snapshot-manager.
973 OnDidCreateMetricsLog();
974 task_environment()->RunUntilIdle();
975 {
976 HistogramFlattenerDeltaRecorder flattener;
977 base::HistogramSnapshotManager snapshot_manager(&flattener);
978 RecordInitialHistogramSnapshots(&snapshot_manager);
979 EXPECT_EQ(0U, flattener.GetRecordedDeltaHistogramNames().size());
980
981 // A read of metrics with internal profiles should return one result, and
982 // the independent log generated should have the embedded system profile.
983 ChromeUserMetricsExtension uma_proto;
984 EXPECT_TRUE(HasIndependentMetrics());
985 EXPECT_TRUE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
986 ASSERT_TRUE(uma_proto.has_system_profile());
987 ASSERT_EQ(1, uma_proto.system_profile().field_trial_size());
988 EXPECT_EQ(123U, uma_proto.system_profile().field_trial(0).name_id());
989 EXPECT_EQ(456U, uma_proto.system_profile().field_trial(0).group_id());
990 EXPECT_EQ(789U, uma_proto.system_profile().session_hash());
991 EXPECT_FALSE(HasIndependentMetrics());
992 EXPECT_FALSE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
993 }
994 task_environment()->RunUntilIdle();
995 EXPECT_FALSE(base::PathExists(metrics_file()));
996 }
997
TEST_P(FileMetricsProviderTest,AccessEmbeddedFallbackMetricsWithoutProfile)998 TEST_P(FileMetricsProviderTest, AccessEmbeddedFallbackMetricsWithoutProfile) {
999 ASSERT_FALSE(PathExists(metrics_file()));
1000 CreateMetricsFileWithHistograms(2);
1001
1002 // Register the file and allow the "checker" task to run.
1003 ASSERT_TRUE(PathExists(metrics_file()));
1004 provider()->RegisterSource(FileMetricsProvider::Params(
1005 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
1006 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN,
1007 kMetricsName));
1008
1009 // Record embedded snapshots via snapshot-manager.
1010 ASSERT_TRUE(HasPreviousSessionData());
1011 task_environment()->RunUntilIdle();
1012 {
1013 HistogramFlattenerDeltaRecorder flattener;
1014 base::HistogramSnapshotManager snapshot_manager(&flattener);
1015 RecordInitialHistogramSnapshots(&snapshot_manager);
1016 EXPECT_EQ(2U, flattener.GetRecordedDeltaHistogramNames().size());
1017
1018 // A read of metrics with internal profiles should return nothing.
1019 ChromeUserMetricsExtension uma_proto;
1020 EXPECT_FALSE(HasIndependentMetrics());
1021 EXPECT_FALSE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
1022 }
1023 EXPECT_TRUE(base::PathExists(metrics_file()));
1024 OnDidCreateMetricsLog();
1025 task_environment()->RunUntilIdle();
1026 EXPECT_FALSE(base::PathExists(metrics_file()));
1027 }
1028
TEST_P(FileMetricsProviderTest,AccessEmbeddedFallbackMetricsWithProfile)1029 TEST_P(FileMetricsProviderTest, AccessEmbeddedFallbackMetricsWithProfile) {
1030 ASSERT_FALSE(PathExists(metrics_file()));
1031 CreateMetricsFileWithHistograms(
1032 metrics_file(), base::Time::Now(), 2,
1033 base::BindOnce(&WriteSystemProfileToAllocator));
1034
1035 // Register the file and allow the "checker" task to run.
1036 ASSERT_TRUE(PathExists(metrics_file()));
1037 provider()->RegisterSource(FileMetricsProvider::Params(
1038 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
1039 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE_OR_PREVIOUS_RUN,
1040 kMetricsName));
1041
1042 // Record embedded snapshots via snapshot-manager.
1043 EXPECT_FALSE(HasPreviousSessionData());
1044 task_environment()->RunUntilIdle();
1045 {
1046 HistogramFlattenerDeltaRecorder flattener;
1047 base::HistogramSnapshotManager snapshot_manager(&flattener);
1048 RecordInitialHistogramSnapshots(&snapshot_manager);
1049 EXPECT_EQ(0U, flattener.GetRecordedDeltaHistogramNames().size());
1050
1051 // A read of metrics with internal profiles should return one result.
1052 ChromeUserMetricsExtension uma_proto;
1053 EXPECT_TRUE(HasIndependentMetrics());
1054 EXPECT_TRUE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
1055 EXPECT_FALSE(HasIndependentMetrics());
1056 EXPECT_FALSE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
1057 }
1058 task_environment()->RunUntilIdle();
1059 EXPECT_FALSE(base::PathExists(metrics_file()));
1060 }
1061
TEST_P(FileMetricsProviderTest,AccessEmbeddedProfileMetricsFromDir)1062 TEST_P(FileMetricsProviderTest, AccessEmbeddedProfileMetricsFromDir) {
1063 const int file_count = 3;
1064 base::Time file_base_time = base::Time::Now();
1065 std::vector<base::FilePath> file_names;
1066 for (int i = 0; i < file_count; ++i) {
1067 CreateMetricsFileWithHistograms(
1068 metrics_file(), base::Time::Now(), 2,
1069 base::BindOnce(&WriteSystemProfileToAllocator));
1070 ASSERT_TRUE(PathExists(metrics_file()));
1071 char new_name[] = "hX";
1072 new_name[1] = '1' + i;
1073 base::FilePath file_name = temp_dir().AppendASCII(new_name).AddExtension(
1074 base::PersistentMemoryAllocator::kFileExtension);
1075 base::Time file_time = file_base_time - base::Minutes(file_count - i);
1076 base::TouchFile(metrics_file(), file_time, file_time);
1077 base::Move(metrics_file(), file_name);
1078 file_names.push_back(std::move(file_name));
1079 }
1080
1081 // Register the file and allow the "checker" task to run.
1082 provider()->RegisterSource(FileMetricsProvider::Params(
1083 temp_dir(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_DIR,
1084 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE));
1085
1086 OnDidCreateMetricsLog();
1087 task_environment()->RunUntilIdle();
1088
1089 // A read of metrics with internal profiles should return one result.
1090 HistogramFlattenerDeltaRecorder flattener;
1091 base::HistogramSnapshotManager snapshot_manager(&flattener);
1092 ChromeUserMetricsExtension uma_proto;
1093 for (int i = 0; i < file_count; ++i) {
1094 EXPECT_TRUE(HasIndependentMetrics()) << i;
1095 EXPECT_TRUE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager)) << i;
1096 task_environment()->RunUntilIdle();
1097 }
1098 EXPECT_FALSE(HasIndependentMetrics());
1099 EXPECT_FALSE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
1100
1101 OnDidCreateMetricsLog();
1102 task_environment()->RunUntilIdle();
1103 for (const auto& file_name : file_names)
1104 EXPECT_FALSE(base::PathExists(file_name));
1105 }
1106
TEST_P(FileMetricsProviderTest,RecordInitialHistogramSnapshotsStabilityHistograms)1107 TEST_P(FileMetricsProviderTest,
1108 RecordInitialHistogramSnapshotsStabilityHistograms) {
1109 // Create a metrics file with 2 non-stability histograms and 2 stability
1110 // histograms. Histogram names must be 2 characters (see
1111 // HistogramFlattenerDeltaRecorder).
1112 ASSERT_FALSE(PathExists(metrics_file()));
1113 base::GlobalHistogramAllocator::CreateWithLocalMemory(
1114 create_large_files_ ? kLargeFileSize : kSmallFileSize, 0, kMetricsName);
1115 base::HistogramBase* h0 = base::SparseHistogram::FactoryGet(
1116 "h0", /*flags=*/base::HistogramBase::Flags::kUmaStabilityHistogramFlag);
1117 h0->Add(0);
1118 base::HistogramBase* h1 = base::SparseHistogram::FactoryGet(
1119 "h1", /*flags=*/base::HistogramBase::Flags::kUmaTargetedHistogramFlag);
1120 h1->Add(0);
1121 base::HistogramBase* h2 = base::Histogram::FactoryGet(
1122 "h2", 1, 100, 10,
1123 /*flags=*/base::HistogramBase::Flags::kUmaStabilityHistogramFlag);
1124 h2->Add(0);
1125 base::HistogramBase* h3 = base::Histogram::FactoryGet(
1126 "h3", 1, 100, 10,
1127 /*flags=*/base::HistogramBase::Flags::kUmaTargetedHistogramFlag);
1128 h3->Add(0);
1129 base::GlobalHistogramAllocator* histogram_allocator =
1130 base::GlobalHistogramAllocator::ReleaseForTesting();
1131 WriteMetricsFileAtTime(metrics_file(), histogram_allocator,
1132 base::Time::Now());
1133 ASSERT_TRUE(PathExists(metrics_file()));
1134
1135 // Register the file and allow the "checker" task to run.
1136 provider()->RegisterSource(FileMetricsProvider::Params(
1137 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
1138 FileMetricsProvider::ASSOCIATE_PREVIOUS_RUN, kMetricsName));
1139 ASSERT_TRUE(HasPreviousSessionData());
1140 task_environment()->RunUntilIdle();
1141
1142 // Record embedded snapshots via snapshot-manager.
1143 HistogramFlattenerDeltaRecorder flattener;
1144 base::HistogramSnapshotManager snapshot_manager(&flattener);
1145 RecordInitialHistogramSnapshots(&snapshot_manager);
1146
1147 // Verify that only the stability histograms were snapshotted.
1148 EXPECT_THAT(flattener.GetRecordedDeltaHistogramNames(),
1149 testing::ElementsAre("h0", "h2"));
1150
1151 // The metrics file should eventually be deleted.
1152 EXPECT_TRUE(base::PathExists(metrics_file()));
1153 OnDidCreateMetricsLog();
1154 task_environment()->RunUntilIdle();
1155 EXPECT_FALSE(base::PathExists(metrics_file()));
1156 }
1157
TEST_P(FileMetricsProviderTest,IndependentLogContainsUmaHistograms)1158 TEST_P(FileMetricsProviderTest, IndependentLogContainsUmaHistograms) {
1159 ASSERT_FALSE(PathExists(metrics_file()));
1160 // Create a metrics file with 2 UMA histograms and 2 non-UMA histograms.
1161 // Histogram names must be 2 characters (see HistogramFlattenerDeltaRecorder).
1162 base::GlobalHistogramAllocator::CreateWithLocalMemory(
1163 create_large_files_ ? kLargeFileSize : kSmallFileSize, 0, kMetricsName);
1164 base::HistogramBase* h0 = base::SparseHistogram::FactoryGet(
1165 "h0", /*flags=*/base::HistogramBase::Flags::kUmaTargetedHistogramFlag);
1166 h0->Add(0);
1167 base::HistogramBase* h1 = base::SparseHistogram::FactoryGet(
1168 "h1", /*flags=*/base::HistogramBase::Flags::kNoFlags);
1169 h1->Add(0);
1170 base::HistogramBase* h2 = base::Histogram::FactoryGet(
1171 "h2", 1, 100, 10,
1172 /*flags=*/base::HistogramBase::Flags::kUmaStabilityHistogramFlag);
1173 h2->Add(0);
1174 base::HistogramBase* h3 = base::Histogram::FactoryGet(
1175 "h3", 1, 100, 10,
1176 /*flags=*/base::HistogramBase::Flags::kNoFlags);
1177 h3->Add(0);
1178 base::GlobalHistogramAllocator* histogram_allocator =
1179 base::GlobalHistogramAllocator::ReleaseForTesting();
1180 // Write a system profile so that an independent log can successfully be
1181 // created from the metrics file.
1182 WriteSystemProfileToAllocator(histogram_allocator);
1183 WriteMetricsFileAtTime(metrics_file(), histogram_allocator,
1184 base::Time::Now());
1185 ASSERT_TRUE(PathExists(metrics_file()));
1186
1187 // Register the file and allow the "checker" task to run.
1188 provider()->RegisterSource(FileMetricsProvider::Params(
1189 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
1190 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName));
1191 OnDidCreateMetricsLog();
1192 task_environment()->RunUntilIdle();
1193
1194 // Verify that the independent log provided only contains UMA histograms (both
1195 // stability and non-stability).
1196 ChromeUserMetricsExtension uma_proto;
1197 HistogramFlattenerDeltaRecorder flattener;
1198 base::HistogramSnapshotManager snapshot_manager(&flattener);
1199 EXPECT_TRUE(HasIndependentMetrics());
1200 EXPECT_TRUE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
1201 EXPECT_THAT(flattener.GetRecordedDeltaHistogramNames(),
1202 testing::ElementsAre("h0", "h2"));
1203
1204 // The metrics file should eventually be deleted.
1205 task_environment()->RunUntilIdle();
1206 EXPECT_FALSE(base::PathExists(metrics_file()));
1207 }
1208
1209 // Verifies that if the embedded system profile in the file does not contain
1210 // a client UUID, the generated independent log's client ID is not overwritten.
TEST_P(FileMetricsProviderTest,EmbeddedProfileWithoutClientUuid)1211 TEST_P(FileMetricsProviderTest, EmbeddedProfileWithoutClientUuid) {
1212 ASSERT_FALSE(PathExists(metrics_file()));
1213 CreateMetricsFileWithHistograms(
1214 metrics_file(), base::Time::Now(), 2,
1215 base::BindOnce(&WriteSystemProfileToAllocator));
1216
1217 // Register the file and allow the "checker" task to run.
1218 ASSERT_TRUE(PathExists(metrics_file()));
1219 provider()->RegisterSource(FileMetricsProvider::Params(
1220 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
1221 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName));
1222
1223 // Record embedded snapshots via snapshot-manager.
1224 OnDidCreateMetricsLog();
1225 task_environment()->RunUntilIdle();
1226 {
1227 HistogramFlattenerDeltaRecorder flattener;
1228 base::HistogramSnapshotManager snapshot_manager(&flattener);
1229
1230 // Since the embedded system profile has no client_uuid set (see
1231 // WriteSystemProfileToAllocator()), the client ID written in |uma_proto|
1232 // should be kept.
1233 ChromeUserMetricsExtension uma_proto;
1234 uma_proto.set_client_id(1);
1235 EXPECT_TRUE(HasIndependentMetrics());
1236 EXPECT_TRUE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
1237 EXPECT_EQ(uma_proto.client_id(), 1U);
1238 }
1239 task_environment()->RunUntilIdle();
1240 EXPECT_FALSE(base::PathExists(metrics_file()));
1241 }
1242
1243 // Verifies that if the embedded system profile in the file contains a client
1244 // UUID, it is used as the generated independent log's client ID.
TEST_P(FileMetricsProviderTest,EmbeddedProfileWithClientUuid)1245 TEST_P(FileMetricsProviderTest, EmbeddedProfileWithClientUuid) {
1246 ASSERT_FALSE(PathExists(metrics_file()));
1247 static constexpr char kProfileClientUuid[] = "abc";
1248 CreateMetricsFileWithHistograms(
1249 metrics_file(), base::Time::Now(), 2,
1250 base::BindOnce([](base::PersistentHistogramAllocator* allocator) {
1251 metrics::SystemProfileProto profile_proto;
1252 profile_proto.set_client_uuid(kProfileClientUuid);
1253
1254 metrics::PersistentSystemProfile persistent_profile;
1255 persistent_profile.RegisterPersistentAllocator(
1256 allocator->memory_allocator());
1257 persistent_profile.SetSystemProfile(profile_proto, /*complete=*/true);
1258 }));
1259
1260 // Register the file and allow the "checker" task to run.
1261 ASSERT_TRUE(PathExists(metrics_file()));
1262 provider()->RegisterSource(FileMetricsProvider::Params(
1263 metrics_file(), FileMetricsProvider::SOURCE_HISTOGRAMS_ATOMIC_FILE,
1264 FileMetricsProvider::ASSOCIATE_INTERNAL_PROFILE, kMetricsName));
1265
1266 // Record embedded snapshots via snapshot-manager.
1267 OnDidCreateMetricsLog();
1268 task_environment()->RunUntilIdle();
1269 {
1270 HistogramFlattenerDeltaRecorder flattener;
1271 base::HistogramSnapshotManager snapshot_manager(&flattener);
1272
1273 // Since the embedded system profile contains a client_uuid, the client ID
1274 // in |uma_proto| should be overwritten.
1275 ChromeUserMetricsExtension uma_proto;
1276 uma_proto.set_client_id(1);
1277 EXPECT_TRUE(HasIndependentMetrics());
1278 EXPECT_TRUE(ProvideIndependentMetrics(&uma_proto, &snapshot_manager));
1279 EXPECT_NE(uma_proto.client_id(), 1U);
1280 EXPECT_EQ(uma_proto.client_id(), MetricsLog::Hash(kProfileClientUuid));
1281 }
1282 task_environment()->RunUntilIdle();
1283 EXPECT_FALSE(base::PathExists(metrics_file()));
1284 }
1285
1286 } // namespace metrics
1287