168 lines
6.2 KiB
C++
168 lines
6.2 KiB
C++
|
|
// Copyright 2016 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "components/metrics/content/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/browser_task_environment.h"
|
||
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
||
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
||
|
|
|
||
|
|
using ::testing::IsEmpty;
|
||
|
|
using ::testing::UnorderedElementsAre;
|
||
|
|
|
||
|
|
namespace metrics {
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
const uint32_t TEST_MEMORY_SIZE = 64 << 10; // 64 KiB
|
||
|
|
|
||
|
|
class HistogramFlattenerDeltaRecorder : public base::HistogramFlattener {
|
||
|
|
public:
|
||
|
|
HistogramFlattenerDeltaRecorder() {}
|
||
|
|
|
||
|
|
HistogramFlattenerDeltaRecorder(const HistogramFlattenerDeltaRecorder&) =
|
||
|
|
delete;
|
||
|
|
HistogramFlattenerDeltaRecorder& operator=(
|
||
|
|
const HistogramFlattenerDeltaRecorder&) = delete;
|
||
|
|
|
||
|
|
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_;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
class SubprocessMetricsProviderTest : public testing::Test {
|
||
|
|
public:
|
||
|
|
SubprocessMetricsProviderTest(const SubprocessMetricsProviderTest&) = delete;
|
||
|
|
SubprocessMetricsProviderTest& operator=(
|
||
|
|
const SubprocessMetricsProviderTest&) = delete;
|
||
|
|
|
||
|
|
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::BrowserTaskEnvironment task_environment_;
|
||
|
|
|
||
|
|
SubprocessMetricsProvider provider_;
|
||
|
|
std::unique_ptr<base::StatisticsRecorder> test_recorder_;
|
||
|
|
};
|
||
|
|
|
||
|
|
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());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace metrics
|