// 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/scoped_temp_dir.h" #include "base/logging.h" #include "base/memory/ptr_util.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/gtest/include/gtest/gtest.h" namespace base { class PersistentHistogramAllocatorTest : public testing::Test { 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::GetCreateHistogramResultHistogram(); 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_; PersistentMemoryAllocator* allocator_ = nullptr; private: DISALLOW_COPY_AND_ASSIGN(PersistentHistogramAllocatorTest); }; TEST_F(PersistentHistogramAllocatorTest, CreateAndIterateTest) { 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( WrapUnique(new 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, CreateWithFileTest) { const char temp_name[] = "CreateWithFileTest"; ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); FilePath temp_file = temp_dir.path().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, StatisticsRecorderTest) { size_t starting_sr_count = StatisticsRecorder::GetHistogramCount(); // Create a local StatisticsRecorder in which the newly created histogram // will be recorded. std::unique_ptr<StatisticsRecorder> local_sr = StatisticsRecorder::CreateTemporaryForTesting(); EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount()); HistogramBase* histogram = LinearHistogram::FactoryGet( "TestHistogram", 1, 10, 10, HistogramBase::kIsPersistent); EXPECT_TRUE(histogram); EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount()); histogram->Add(3); histogram->Add(1); histogram->Add(4); histogram->Add(1); histogram->Add(6); // Destroy the local SR and ensure that we're back to the initial state. local_sr.reset(); EXPECT_EQ(starting_sr_count, StatisticsRecorder::GetHistogramCount()); // Create a second allocator and have it access the memory of the first. std::unique_ptr<HistogramBase> recovered; PersistentHistogramAllocator recovery( WrapUnique(new PersistentMemoryAllocator( allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false))); PersistentHistogramAllocator::Iterator histogram_iter(&recovery); recovered = histogram_iter.GetNext(); ASSERT_TRUE(recovered); // Merge the recovered histogram to the SR. It will always be a new object. recovery.MergeHistogramDeltaToStatisticsRecorder(recovered.get()); EXPECT_EQ(starting_sr_count + 1, StatisticsRecorder::GetHistogramCount()); HistogramBase* found = StatisticsRecorder::FindHistogram(recovered->histogram_name()); ASSERT_TRUE(found); EXPECT_NE(recovered.get(), found); // Ensure that the data got merged, too. std::unique_ptr<HistogramSamples> snapshot = found->SnapshotSamples(); EXPECT_EQ(recovered->SnapshotSamples()->TotalCount(), snapshot->TotalCount()); EXPECT_EQ(1, snapshot->GetCount(3)); EXPECT_EQ(2, snapshot->GetCount(1)); EXPECT_EQ(1, snapshot->GetCount(4)); EXPECT_EQ(1, snapshot->GetCount(6)); } } // namespace base