blob: f60583b128086ab8bd300e7fbb6899e4dcf7a98a [file] [log] [blame]
// 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 "chrome/browser/metrics/subprocess_metrics_provider.h"
#include <memory>
#include <string>
#include <vector>
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_flattener.h"
#include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/metrics/persistent_memory_allocator.h"
#include "base/metrics/statistics_recorder.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::UnorderedElementsAre;
using ::testing::IsEmpty;
namespace {
const uint32_t TEST_MEMORY_SIZE = 64 << 10; // 64 KiB
class HistogramFlattenerDeltaRecorder : public base::HistogramFlattener {
public:
HistogramFlattenerDeltaRecorder() {}
void RecordDelta(const base::HistogramBase& histogram,
const base::HistogramSamples& snapshot) override {
recorded_delta_histogram_names_.push_back(histogram.histogram_name());
}
std::vector<std::string> GetRecordedDeltaHistogramNames() {
return recorded_delta_histogram_names_;
}
private:
std::vector<std::string> recorded_delta_histogram_names_;
DISALLOW_COPY_AND_ASSIGN(HistogramFlattenerDeltaRecorder);
};
} // namespace
class SubprocessMetricsProviderTest : public testing::Test {
protected:
SubprocessMetricsProviderTest() {
// MergeHistogramDeltas needs to be called beause it uses a histogram
// macro which caches a pointer to a histogram. If not done before setting
// a persistent global allocator, then it would point into memory that
// will go away.
provider_.MergeHistogramDeltas();
// Create a dedicated StatisticsRecorder for this test.
test_recorder_ = base::StatisticsRecorder::CreateTemporaryForTesting();
// Create a global allocator using a block of memory from the heap.
base::GlobalHistogramAllocator::CreateWithLocalMemory(TEST_MEMORY_SIZE,
0, "");
}
~SubprocessMetricsProviderTest() override {
base::GlobalHistogramAllocator::ReleaseForTesting();
}
SubprocessMetricsProvider* provider() { return &provider_; }
std::unique_ptr<base::PersistentHistogramAllocator> CreateDuplicateAllocator(
base::PersistentHistogramAllocator* allocator) {
// Just wrap around the data segment in-use by the passed allocator.
return std::make_unique<base::PersistentHistogramAllocator>(
std::make_unique<base::PersistentMemoryAllocator>(
const_cast<void*>(allocator->data()), allocator->length(), 0, 0,
std::string(), false));
}
std::vector<std::string> GetSnapshotHistogramNames() {
// Merge the data from the allocator into the StatisticsRecorder.
provider_.MergeHistogramDeltas();
// Flatten what is known to see what has changed since the last time.
HistogramFlattenerDeltaRecorder flattener;
base::HistogramSnapshotManager snapshot_manager(&flattener);
// "true" to the begin() includes histograms held in persistent storage.
base::StatisticsRecorder::PrepareDeltas(true, base::Histogram::kNoFlags,
base::Histogram::kNoFlags,
&snapshot_manager);
return flattener.GetRecordedDeltaHistogramNames();
}
void EnableRecording() { provider_.OnRecordingEnabled(); }
void DisableRecording() { provider_.OnRecordingDisabled(); }
void RegisterSubprocessAllocator(
int id,
std::unique_ptr<base::PersistentHistogramAllocator> allocator) {
provider_.RegisterSubprocessAllocator(id, std::move(allocator));
}
void DeregisterSubprocessAllocator(int id) {
provider_.DeregisterSubprocessAllocator(id);
}
private:
// A thread-bundle makes the tests appear on the UI thread, something that is
// checked in methods called from the SubprocessMetricsProvider class under
// test. This must be constructed before the |provider_| field.
content::TestBrowserThreadBundle thread_bundle_;
SubprocessMetricsProvider provider_;
std::unique_ptr<base::StatisticsRecorder> test_recorder_;
DISALLOW_COPY_AND_ASSIGN(SubprocessMetricsProviderTest);
};
TEST_F(SubprocessMetricsProviderTest, SnapshotMetrics) {
base::HistogramBase* foo = base::Histogram::FactoryGet("foo", 1, 100, 10, 0);
base::HistogramBase* bar = base::Histogram::FactoryGet("bar", 1, 100, 10, 0);
base::HistogramBase* baz = base::Histogram::FactoryGet("baz", 1, 100, 10, 0);
foo->Add(42);
bar->Add(84);
// Detach the global allocator but keep it around until this method exits
// so that the memory holding histogram data doesn't get released. Register
// a new allocator that duplicates the global one.
std::unique_ptr<base::GlobalHistogramAllocator> global_allocator(
base::GlobalHistogramAllocator::ReleaseForTesting());
RegisterSubprocessAllocator(123,
CreateDuplicateAllocator(global_allocator.get()));
// Recording should find the two histograms created in persistent memory.
EXPECT_THAT(GetSnapshotHistogramNames(), UnorderedElementsAre("foo", "bar"));
// A second run should have nothing to produce.
EXPECT_THAT(GetSnapshotHistogramNames(), IsEmpty());
// Create a new histogram and update existing ones. Should now report 3 items.
baz->Add(1969);
foo->Add(10);
bar->Add(20);
EXPECT_THAT(GetSnapshotHistogramNames(),
UnorderedElementsAre("foo", "bar", "baz"));
// Ensure that deregistering does a final merge of the data.
foo->Add(10);
bar->Add(20);
DeregisterSubprocessAllocator(123);
EXPECT_THAT(GetSnapshotHistogramNames(), UnorderedElementsAre("foo", "bar"));
// Further snapshots should be empty even if things have changed.
foo->Add(10);
bar->Add(20);
EXPECT_THAT(GetSnapshotHistogramNames(), IsEmpty());
}