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