123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/metrics/persistent_histogram_allocator.h"
- #include "base/files/file.h"
- #include "base/files/file_util.h"
- #include "base/files/scoped_temp_dir.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/raw_ptr.h"
- #include "base/metrics/bucket_ranges.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/metrics/persistent_memory_allocator.h"
- #include "base/metrics/statistics_recorder.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace base {
- class PersistentHistogramAllocatorTest : public testing::Test {
- public:
- PersistentHistogramAllocatorTest(const PersistentHistogramAllocatorTest&) =
- delete;
- PersistentHistogramAllocatorTest& operator=(
- const PersistentHistogramAllocatorTest&) = delete;
- protected:
- const int32_t kAllocatorMemorySize = 64 << 10; // 64 KiB
- PersistentHistogramAllocatorTest()
- : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting()) {
- CreatePersistentHistogramAllocator();
- }
- ~PersistentHistogramAllocatorTest() override {
- DestroyPersistentHistogramAllocator();
- }
- void CreatePersistentHistogramAllocator() {
- allocator_memory_.reset(new char[kAllocatorMemorySize]);
- GlobalHistogramAllocator::ReleaseForTesting();
- memset(allocator_memory_.get(), 0, kAllocatorMemorySize);
- GlobalHistogramAllocator::CreateWithPersistentMemory(
- allocator_memory_.get(), kAllocatorMemorySize, 0, 0,
- "PersistentHistogramAllocatorTest");
- allocator_ = GlobalHistogramAllocator::Get()->memory_allocator();
- }
- void DestroyPersistentHistogramAllocator() {
- allocator_ = nullptr;
- GlobalHistogramAllocator::ReleaseForTesting();
- }
- std::unique_ptr<StatisticsRecorder> statistics_recorder_;
- std::unique_ptr<char[]> allocator_memory_;
- raw_ptr<PersistentMemoryAllocator> allocator_ = nullptr;
- };
- TEST_F(PersistentHistogramAllocatorTest, CreateAndIterate) {
- PersistentMemoryAllocator::MemoryInfo meminfo0;
- allocator_->GetMemoryInfo(&meminfo0);
- // Try basic construction
- HistogramBase* histogram = Histogram::FactoryGet(
- "TestHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
- EXPECT_TRUE(histogram);
- histogram->CheckName("TestHistogram");
- PersistentMemoryAllocator::MemoryInfo meminfo1;
- allocator_->GetMemoryInfo(&meminfo1);
- EXPECT_GT(meminfo0.free, meminfo1.free);
- HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
- "TestLinearHistogram", 1, 1000, 10, HistogramBase::kIsPersistent);
- EXPECT_TRUE(linear_histogram);
- linear_histogram->CheckName("TestLinearHistogram");
- PersistentMemoryAllocator::MemoryInfo meminfo2;
- allocator_->GetMemoryInfo(&meminfo2);
- EXPECT_GT(meminfo1.free, meminfo2.free);
- HistogramBase* boolean_histogram = BooleanHistogram::FactoryGet(
- "TestBooleanHistogram", HistogramBase::kIsPersistent);
- EXPECT_TRUE(boolean_histogram);
- boolean_histogram->CheckName("TestBooleanHistogram");
- PersistentMemoryAllocator::MemoryInfo meminfo3;
- allocator_->GetMemoryInfo(&meminfo3);
- EXPECT_GT(meminfo2.free, meminfo3.free);
- std::vector<int> custom_ranges;
- custom_ranges.push_back(1);
- custom_ranges.push_back(5);
- HistogramBase* custom_histogram = CustomHistogram::FactoryGet(
- "TestCustomHistogram", custom_ranges, HistogramBase::kIsPersistent);
- EXPECT_TRUE(custom_histogram);
- custom_histogram->CheckName("TestCustomHistogram");
- PersistentMemoryAllocator::MemoryInfo meminfo4;
- allocator_->GetMemoryInfo(&meminfo4);
- EXPECT_GT(meminfo3.free, meminfo4.free);
- PersistentMemoryAllocator::Iterator iter(allocator_);
- uint32_t type;
- EXPECT_NE(0U, iter.GetNext(&type)); // Histogram
- EXPECT_NE(0U, iter.GetNext(&type)); // LinearHistogram
- EXPECT_NE(0U, iter.GetNext(&type)); // BooleanHistogram
- EXPECT_NE(0U, iter.GetNext(&type)); // CustomHistogram
- EXPECT_EQ(0U, iter.GetNext(&type));
- // Create a second allocator and have it access the memory of the first.
- std::unique_ptr<HistogramBase> recovered;
- PersistentHistogramAllocator recovery(
- std::make_unique<PersistentMemoryAllocator>(
- allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false));
- PersistentHistogramAllocator::Iterator histogram_iter(&recovery);
- recovered = histogram_iter.GetNext();
- ASSERT_TRUE(recovered);
- recovered->CheckName("TestHistogram");
- recovered = histogram_iter.GetNext();
- ASSERT_TRUE(recovered);
- recovered->CheckName("TestLinearHistogram");
- recovered = histogram_iter.GetNext();
- ASSERT_TRUE(recovered);
- recovered->CheckName("TestBooleanHistogram");
- recovered = histogram_iter.GetNext();
- ASSERT_TRUE(recovered);
- recovered->CheckName("TestCustomHistogram");
- recovered = histogram_iter.GetNext();
- EXPECT_FALSE(recovered);
- }
- TEST_F(PersistentHistogramAllocatorTest, ConstructPaths) {
- const FilePath dir_path(FILE_PATH_LITERAL("foo/"));
- const std::string dir_string =
- dir_path.NormalizePathSeparators().AsUTF8Unsafe();
- FilePath path = GlobalHistogramAllocator::ConstructFilePath(dir_path, "bar");
- EXPECT_EQ(dir_string + "bar.pma", path.AsUTF8Unsafe());
- std::string name;
- Time stamp;
- ProcessId pid;
- EXPECT_FALSE(
- GlobalHistogramAllocator::ParseFilePath(path, &name, nullptr, nullptr));
- EXPECT_FALSE(
- GlobalHistogramAllocator::ParseFilePath(path, nullptr, &stamp, nullptr));
- EXPECT_FALSE(
- GlobalHistogramAllocator::ParseFilePath(path, nullptr, nullptr, &pid));
- path = GlobalHistogramAllocator::ConstructFilePathForUploadDir(
- dir_path, "bar", Time::FromTimeT(12345), 6789);
- EXPECT_EQ(dir_string + "bar-3039-1A85.pma", path.AsUTF8Unsafe());
- ASSERT_TRUE(
- GlobalHistogramAllocator::ParseFilePath(path, &name, &stamp, &pid));
- EXPECT_EQ(name, "bar");
- EXPECT_EQ(Time::FromTimeT(12345), stamp);
- EXPECT_EQ(static_cast<ProcessId>(6789), pid);
- }
- TEST_F(PersistentHistogramAllocatorTest, CreateWithFile) {
- const char temp_name[] = "CreateWithFileTest";
- ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
- const size_t temp_size = 64 << 10; // 64 KiB
- // Test creation of a new file.
- GlobalHistogramAllocator::ReleaseForTesting();
- GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, temp_name);
- EXPECT_EQ(std::string(temp_name),
- GlobalHistogramAllocator::Get()->memory_allocator()->Name());
- // Test re-open of a possibly-existing file.
- GlobalHistogramAllocator::ReleaseForTesting();
- GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, "");
- EXPECT_EQ(std::string(temp_name),
- GlobalHistogramAllocator::Get()->memory_allocator()->Name());
- // Test re-open of an known-existing file.
- GlobalHistogramAllocator::ReleaseForTesting();
- GlobalHistogramAllocator::CreateWithFile(temp_file, 0, 0, "");
- EXPECT_EQ(std::string(temp_name),
- GlobalHistogramAllocator::Get()->memory_allocator()->Name());
- // Final release so file and temp-dir can be removed.
- GlobalHistogramAllocator::ReleaseForTesting();
- }
- TEST_F(PersistentHistogramAllocatorTest, CreateSpareFile) {
- const char temp_name[] = "CreateSpareFileTest.pma";
- ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name);
- const size_t temp_size = 64 << 10; // 64 KiB
- ASSERT_TRUE(GlobalHistogramAllocator::CreateSpareFile(temp_file, temp_size));
- File file(temp_file, File::FLAG_OPEN | File::FLAG_READ);
- ASSERT_TRUE(file.IsValid());
- EXPECT_EQ(static_cast<int64_t>(temp_size), file.GetLength());
- char buffer[256];
- for (size_t pos = 0; pos < temp_size; pos += sizeof(buffer)) {
- ASSERT_EQ(static_cast<int>(sizeof(buffer)),
- file.ReadAtCurrentPos(buffer, sizeof(buffer)));
- for (size_t i = 0; i < sizeof(buffer); ++i)
- EXPECT_EQ(0, buffer[i]);
- }
- }
- TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMerge) {
- const char LinearHistogramName[] = "SRTLinearHistogram";
- const char SparseHistogramName[] = "SRTSparseHistogram";
- const size_t global_sr_initial_histogram_count =
- StatisticsRecorder::GetHistogramCount();
- const size_t global_sr_initial_bucket_ranges_count =
- StatisticsRecorder::GetBucketRanges().size();
- // Create a local StatisticsRecorder in which the newly created histogram
- // will be recorded. The global allocator must be replaced after because the
- // act of releasing will cause the active SR to forget about all histograms
- // in the relased memory.
- std::unique_ptr<StatisticsRecorder> local_sr =
- StatisticsRecorder::CreateTemporaryForTesting();
- EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
- std::unique_ptr<GlobalHistogramAllocator> old_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
- GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
- ASSERT_TRUE(GlobalHistogramAllocator::Get());
- // Create a linear histogram for merge testing.
- HistogramBase* histogram1 =
- LinearHistogram::FactoryGet(LinearHistogramName, 1, 10, 10, 0);
- ASSERT_TRUE(histogram1);
- EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount());
- histogram1->Add(3);
- histogram1->Add(1);
- histogram1->Add(4);
- histogram1->AddCount(1, 4);
- histogram1->Add(6);
- // Create a sparse histogram for merge testing.
- HistogramBase* histogram2 =
- SparseHistogram::FactoryGet(SparseHistogramName, 0);
- ASSERT_TRUE(histogram2);
- EXPECT_EQ(2U, StatisticsRecorder::GetHistogramCount());
- histogram2->Add(3);
- histogram2->Add(1);
- histogram2->Add(4);
- histogram2->AddCount(1, 4);
- histogram2->Add(6);
- // Destroy the local SR and ensure that we're back to the initial state and
- // restore the global allocator. Histograms created in the local SR will
- // become unmanaged.
- std::unique_ptr<GlobalHistogramAllocator> new_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
- local_sr.reset();
- EXPECT_EQ(global_sr_initial_histogram_count,
- StatisticsRecorder::GetHistogramCount());
- EXPECT_EQ(global_sr_initial_bucket_ranges_count,
- StatisticsRecorder::GetBucketRanges().size());
- GlobalHistogramAllocator::Set(std::move(old_allocator));
- // Create a "recovery" allocator using the same memory as the local one.
- PersistentHistogramAllocator recovery1(
- std::make_unique<PersistentMemoryAllocator>(
- const_cast<void*>(new_allocator->memory_allocator()->data()),
- new_allocator->memory_allocator()->size(), 0, 0, "", false));
- PersistentHistogramAllocator::Iterator histogram_iter1(&recovery1);
- // Get the histograms that were created locally (and forgotten) and merge
- // them into the global SR. New objects will be created.
- std::unique_ptr<HistogramBase> recovered;
- while (true) {
- recovered = histogram_iter1.GetNext();
- if (!recovered)
- break;
- recovery1.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
- HistogramBase* found =
- StatisticsRecorder::FindHistogram(recovered->histogram_name());
- EXPECT_NE(recovered.get(), found);
- }
- EXPECT_EQ(global_sr_initial_histogram_count + 2,
- StatisticsRecorder::GetHistogramCount());
- // Check the merged histograms for accuracy.
- HistogramBase* found = StatisticsRecorder::FindHistogram(LinearHistogramName);
- ASSERT_TRUE(found);
- std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(5, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(4));
- EXPECT_EQ(1, snapshot->GetCount(6));
- found = StatisticsRecorder::FindHistogram(SparseHistogramName);
- ASSERT_TRUE(found);
- snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(5, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(4));
- EXPECT_EQ(1, snapshot->GetCount(6));
- // Verify that the LinearHistogram's BucketRanges was registered with the
- // global SR since the recovery allocator does not specify a custom
- // RangesManager.
- ASSERT_EQ(global_sr_initial_bucket_ranges_count + 1,
- StatisticsRecorder::GetBucketRanges().size());
- // Perform additional histogram increments.
- histogram1->AddCount(1, 3);
- histogram1->Add(6);
- histogram2->AddCount(1, 3);
- histogram2->Add(7);
- // Do another merge.
- PersistentHistogramAllocator recovery2(
- std::make_unique<PersistentMemoryAllocator>(
- const_cast<void*>(new_allocator->memory_allocator()->data()),
- new_allocator->memory_allocator()->size(), 0, 0, "", false));
- PersistentHistogramAllocator::Iterator histogram_iter2(&recovery2);
- while (true) {
- recovered = histogram_iter2.GetNext();
- if (!recovered)
- break;
- recovery2.MergeHistogramDeltaToStatisticsRecorder(recovered.get());
- }
- EXPECT_EQ(global_sr_initial_histogram_count + 2,
- StatisticsRecorder::GetHistogramCount());
- // And verify.
- found = StatisticsRecorder::FindHistogram(LinearHistogramName);
- snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(8, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(4));
- EXPECT_EQ(2, snapshot->GetCount(6));
- found = StatisticsRecorder::FindHistogram(SparseHistogramName);
- snapshot = found->SnapshotSamples();
- EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount());
- EXPECT_EQ(1, snapshot->GetCount(3));
- EXPECT_EQ(8, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(4));
- EXPECT_EQ(1, snapshot->GetCount(6));
- EXPECT_EQ(1, snapshot->GetCount(7));
- }
- TEST_F(PersistentHistogramAllocatorTest, CustomRangesManager) {
- const char LinearHistogramName[] = "TestLinearHistogram";
- const size_t global_sr_initial_bucket_ranges_count =
- StatisticsRecorder::GetBucketRanges().size();
- // Create a local StatisticsRecorder in which the newly created histogram
- // will be recorded. The global allocator must be replaced after because the
- // act of releasing will cause the active SR to forget about all histograms
- // in the released memory.
- std::unique_ptr<StatisticsRecorder> local_sr =
- StatisticsRecorder::CreateTemporaryForTesting();
- EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount());
- std::unique_ptr<GlobalHistogramAllocator> old_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
- GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, "");
- ASSERT_TRUE(GlobalHistogramAllocator::Get());
- // Create a linear histogram and verify it is registered with the local SR.
- HistogramBase* histogram = LinearHistogram::FactoryGet(
- LinearHistogramName, /*minimum=*/1, /*maximum=*/10, /*bucket_count=*/10,
- /*flags=*/0);
- ASSERT_TRUE(histogram);
- EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount());
- histogram->Add(1);
- // Destroy the local SR and ensure that we're back to the initial state and
- // restore the global allocator. The histogram created in the local SR will
- // become unmanaged.
- std::unique_ptr<GlobalHistogramAllocator> new_allocator =
- GlobalHistogramAllocator::ReleaseForTesting();
- local_sr.reset();
- EXPECT_EQ(global_sr_initial_bucket_ranges_count,
- StatisticsRecorder::GetBucketRanges().size());
- GlobalHistogramAllocator::Set(std::move(old_allocator));
- // Create a "recovery" allocator using the same memory as the local one.
- PersistentHistogramAllocator recovery(
- std::make_unique<PersistentMemoryAllocator>(
- const_cast<void*>(new_allocator->memory_allocator()->data()),
- new_allocator->memory_allocator()->size(), 0, 0, "", false));
- // Set a custom RangesManager for the recovery allocator so that the
- // BucketRanges are not registered with the global SR.
- RangesManager* ranges_manager = new RangesManager();
- recovery.SetRangesManager(ranges_manager);
- EXPECT_EQ(0U, ranges_manager->GetBucketRanges().size());
- // Get the histogram that was created locally (and forgotten).
- PersistentHistogramAllocator::Iterator histogram_iter1(&recovery);
- std::unique_ptr<HistogramBase> recovered = histogram_iter1.GetNext();
- ASSERT_TRUE(recovered);
- // Verify that there are no more histograms.
- ASSERT_FALSE(histogram_iter1.GetNext());
- // Expect that the histogram's BucketRanges was not registered with the global
- // statistics recorder since the recovery allocator specifies a custom
- // RangesManager.
- EXPECT_EQ(global_sr_initial_bucket_ranges_count,
- StatisticsRecorder::GetBucketRanges().size());
- EXPECT_EQ(1U, ranges_manager->GetBucketRanges().size());
- }
- TEST_F(PersistentHistogramAllocatorTest, RangesDeDuplication) {
- // This corresponds to the "ranges_ref" field of the PersistentHistogramData
- // structure defined (privately) inside persistent_histogram_allocator.cc.
- const int kRangesRefIndex = 5;
- // Create two histograms with the same ranges.
- HistogramBase* histogram1 =
- Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, 0);
- HistogramBase* histogram2 =
- Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, 0);
- const uint32_t ranges_ref = static_cast<Histogram*>(histogram1)
- ->bucket_ranges()
- ->persistent_reference();
- ASSERT_NE(0U, ranges_ref);
- EXPECT_EQ(ranges_ref, static_cast<Histogram*>(histogram2)
- ->bucket_ranges()
- ->persistent_reference());
- // Make sure that the persistent data record is also correct. Two histograms
- // will be fetched; other allocations are not "iterable".
- PersistentMemoryAllocator::Iterator iter(allocator_);
- uint32_t type;
- uint32_t ref1 = iter.GetNext(&type);
- uint32_t ref2 = iter.GetNext(&type);
- EXPECT_EQ(0U, iter.GetNext(&type));
- EXPECT_NE(0U, ref1);
- EXPECT_NE(0U, ref2);
- EXPECT_NE(ref1, ref2);
- uint32_t* data1 =
- allocator_->GetAsArray<uint32_t>(ref1, 0, kRangesRefIndex + 1);
- uint32_t* data2 =
- allocator_->GetAsArray<uint32_t>(ref2, 0, kRangesRefIndex + 1);
- EXPECT_EQ(ranges_ref, data1[kRangesRefIndex]);
- EXPECT_EQ(ranges_ref, data2[kRangesRefIndex]);
- }
- } // namespace base
|