Commit 4a9839a2 authored by dalecurtis's avatar dalecurtis Committed by Commit bot

Add support for single sample metrics.

Single sample metrics offer a mechanism for clients to modify
a histogram sample repeatedly while having it only reported once;
either upon destruction of a helper class or at process termination.

This is helpful since the fast shutdown path simply kills renderer
processes without going through any destructors. In this case we
lose histogram values that might otherwise be reported at that
time.

In media/ code we've created a cumbersome proxy mechanism which
sends histogram values like this to the browser process and records
them there (see WatchTimeReporter and MediaInternals interactions).

Single sample histograms are implemented through a new mojo service
hosted by the browser process that receives samples and logs the
last received sample upon mojo channel closure.

base:: shims are provided so that these metrics can be created by
anyone in the renderer process. For non-renderer processes a default
implementation is provided without the mojo channel behavior.

Usage looks like the following:
  std::unique_ptr<base::SingleSampleMetric> metric(
      base::SingleSampleMetricsFactory::Get()->CreateCustomCountsMetric(
          "Media.VideoRenderer.CadenceChanges", 1, 10, 10));
  metric->SetSample(1);
  metric->SetSample(2);
  metric->SetSample(3);
  metric.reset();

Only the last sample (3) would end up being reported to the histogram.

BUG=689751
TEST=new tests

Review-Url: https://codereview.chromium.org/2687583002
Cr-Commit-Position: refs/heads/master@{#469524}
parent b59f96b2
...@@ -579,6 +579,8 @@ component("base") { ...@@ -579,6 +579,8 @@ component("base") {
"metrics/sample_map.h", "metrics/sample_map.h",
"metrics/sample_vector.cc", "metrics/sample_vector.cc",
"metrics/sample_vector.h", "metrics/sample_vector.h",
"metrics/single_sample_metrics.cc",
"metrics/single_sample_metrics.h",
"metrics/sparse_histogram.cc", "metrics/sparse_histogram.cc",
"metrics/sparse_histogram.h", "metrics/sparse_histogram.h",
"metrics/statistics_recorder.cc", "metrics/statistics_recorder.cc",
...@@ -2055,6 +2057,7 @@ test("base_unittests") { ...@@ -2055,6 +2057,7 @@ test("base_unittests") {
"metrics/persistent_sample_map_unittest.cc", "metrics/persistent_sample_map_unittest.cc",
"metrics/sample_map_unittest.cc", "metrics/sample_map_unittest.cc",
"metrics/sample_vector_unittest.cc", "metrics/sample_vector_unittest.cc",
"metrics/single_sample_metrics_unittest.cc",
"metrics/sparse_histogram_unittest.cc", "metrics/sparse_histogram_unittest.cc",
"metrics/statistics_recorder_unittest.cc", "metrics/statistics_recorder_unittest.cc",
"native_library_unittest.cc", "native_library_unittest.cc",
......
// Copyright 2017 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/single_sample_metrics.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram.h"
namespace base {
static SingleSampleMetricsFactory* g_factory = nullptr;
// static
SingleSampleMetricsFactory* SingleSampleMetricsFactory::Get() {
if (!g_factory)
g_factory = new DefaultSingleSampleMetricsFactory();
return g_factory;
}
// static
void SingleSampleMetricsFactory::SetFactory(
std::unique_ptr<SingleSampleMetricsFactory> factory) {
DCHECK(!g_factory);
g_factory = factory.release();
}
// static
void SingleSampleMetricsFactory::DeleteFactoryForTesting() {
DCHECK(g_factory);
delete g_factory;
g_factory = nullptr;
}
std::unique_ptr<SingleSampleMetric>
DefaultSingleSampleMetricsFactory::CreateCustomCountsMetric(
const std::string& histogram_name,
HistogramBase::Sample min,
HistogramBase::Sample max,
uint32_t bucket_count) {
return MakeUnique<DefaultSingleSampleMetric>(
histogram_name, min, max, bucket_count,
HistogramBase::kUmaTargetedHistogramFlag);
}
DefaultSingleSampleMetric::DefaultSingleSampleMetric(
const std::string& histogram_name,
HistogramBase::Sample min,
HistogramBase::Sample max,
uint32_t bucket_count,
int32_t flags)
: histogram_(Histogram::FactoryGet(histogram_name,
min,
max,
bucket_count,
flags)) {
// Bad construction parameters may lead to |histogram_| being null; DCHECK to
// find accidental errors in production. We must still handle the nullptr in
// destruction though since this construction may come from another untrusted
// process.
DCHECK(histogram_);
}
DefaultSingleSampleMetric::~DefaultSingleSampleMetric() {
// |histogram_| may be nullptr if bad construction parameters are given.
if (sample_ < 0 || !histogram_)
return;
histogram_->Add(sample_);
}
void DefaultSingleSampleMetric::SetSample(HistogramBase::Sample sample) {
DCHECK_GE(sample, 0);
sample_ = sample;
}
} // namespace base
// Copyright 2017 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.
#ifndef BASE_METRICS_SINGLE_SAMPLE_METRICS_H_
#define BASE_METRICS_SINGLE_SAMPLE_METRICS_H_
#include <string>
#include "base/base_export.h"
#include "base/macros.h"
#include "base/metrics/histogram_base.h"
namespace base {
// See base/metrics/histograms.h for parameter definitions. Must only be used
// and destroyed from the same thread as construction.
class BASE_EXPORT SingleSampleMetric {
public:
virtual ~SingleSampleMetric() {}
virtual void SetSample(HistogramBase::Sample sample) = 0;
};
// Factory for creating single sample metrics. A single sample metric only
// reports its sample once at destruction time. The sample may be changed prior
// to destruction using the SetSample() method as many times as desired.
//
// The metric creation methods are safe to call from any thread, however the
// returned class must only be used and destroyed from the same thread as
// construction.
//
// See base/metrics/histogram_macros.h for usage recommendations and
// base/metrics/histogram.h for full parameter definitions.
class BASE_EXPORT SingleSampleMetricsFactory {
public:
virtual ~SingleSampleMetricsFactory() {}
// Returns the factory provided by SetFactory(), or if no factory has been set
// a default factory will be provided (future calls to SetFactory() will fail
// if the default factory is ever vended).
static SingleSampleMetricsFactory* Get();
static void SetFactory(std::unique_ptr<SingleSampleMetricsFactory> factory);
// The factory normally persists until process shutdown, but in testing we
// should avoid leaking it since it sets a global.
static void DeleteFactoryForTesting();
// The methods below return a single sample metric for counts histograms; see
// method comments for the corresponding histogram macro.
// UMA_HISTOGRAM_CUSTOM_COUNTS()
virtual std::unique_ptr<SingleSampleMetric> CreateCustomCountsMetric(
const std::string& histogram_name,
HistogramBase::Sample min,
HistogramBase::Sample max,
uint32_t bucket_count) = 0;
};
// Default implementation for when no factory has been provided to the process.
// Samples are only recorded within the current process in this case, so samples
// will be lost in the event of sudden process termination.
class BASE_EXPORT DefaultSingleSampleMetricsFactory
: public SingleSampleMetricsFactory {
public:
DefaultSingleSampleMetricsFactory() {}
~DefaultSingleSampleMetricsFactory() override {}
// SingleSampleMetricsFactory:
std::unique_ptr<SingleSampleMetric> CreateCustomCountsMetric(
const std::string& histogram_name,
HistogramBase::Sample min,
HistogramBase::Sample max,
uint32_t bucket_count) override;
private:
DISALLOW_COPY_AND_ASSIGN(DefaultSingleSampleMetricsFactory);
};
class BASE_EXPORT DefaultSingleSampleMetric : public SingleSampleMetric {
public:
DefaultSingleSampleMetric(const std::string& histogram_name,
HistogramBase::Sample min,
HistogramBase::Sample max,
uint32_t bucket_count,
int32_t flags);
~DefaultSingleSampleMetric() override;
// SingleSampleMetric:
void SetSample(HistogramBase::Sample sample) override;
private:
HistogramBase* const histogram_;
// The last sample provided to SetSample(). We use -1 as a sentinel value to
// indicate no sample has been set.
HistogramBase::Sample sample_ = -1;
DISALLOW_COPY_AND_ASSIGN(DefaultSingleSampleMetric);
};
} // namespace base
#endif // BASE_METRICS_SINGLE_SAMPLE_METRICS_H_
// Copyright 2017 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/single_sample_metrics.h"
#include "base/memory/ptr_util.h"
#include "base/test/gtest_util.h"
#include "base/test/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
const HistogramBase::Sample kMin = 1;
const HistogramBase::Sample kMax = 10;
const uint32_t kBucketCount = 10;
const char kMetricName[] = "Single.Sample.Metric";
class SingleSampleMetricsTest : public testing::Test {
public:
SingleSampleMetricsTest() {}
~SingleSampleMetricsTest() override {
// Ensure we cleanup after ourselves.
SingleSampleMetricsFactory::DeleteFactoryForTesting();
}
private:
DISALLOW_COPY_AND_ASSIGN(SingleSampleMetricsTest);
};
} // namespace
TEST_F(SingleSampleMetricsTest, DefaultFactoryGetSet) {
SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get();
ASSERT_TRUE(factory);
// Same factory should be returned evermore.
EXPECT_EQ(factory, SingleSampleMetricsFactory::Get());
// Setting a factory after the default has been instantiated should fail.
EXPECT_DCHECK_DEATH(SingleSampleMetricsFactory::SetFactory(
WrapUnique<SingleSampleMetricsFactory>(nullptr)));
}
TEST_F(SingleSampleMetricsTest, CustomFactoryGetSet) {
SingleSampleMetricsFactory* factory = new DefaultSingleSampleMetricsFactory();
SingleSampleMetricsFactory::SetFactory(WrapUnique(factory));
EXPECT_EQ(factory, SingleSampleMetricsFactory::Get());
}
TEST_F(SingleSampleMetricsTest, DefaultSingleSampleMetricNoValue) {
SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get();
HistogramTester tester;
std::unique_ptr<SingleSampleMetric> metric =
factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
metric.reset();
// Verify that no sample is recorded if SetSample() is never called.
tester.ExpectTotalCount(kMetricName, 0);
}
TEST_F(SingleSampleMetricsTest, DefaultSingleSampleMetricWithValue) {
SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get();
HistogramTester tester;
std::unique_ptr<SingleSampleMetric> metric =
factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
const HistogramBase::Sample kLastSample = 9;
metric->SetSample(1);
metric->SetSample(3);
metric->SetSample(5);
metric->SetSample(kLastSample);
metric.reset();
// Verify only the last sample sent to SetSample() is recorded.
tester.ExpectUniqueSample(kMetricName, kLastSample, 1);
// Verify construction implicitly by requesting a histogram with the same
// parameters; this test relies on the fact that histogram objects are unique
// per name. Different parameters will result in a nullptr being returned.
EXPECT_FALSE(
Histogram::FactoryGet(kMetricName, 1, 3, 3, HistogramBase::kNoFlags));
EXPECT_TRUE(Histogram::FactoryGet(kMetricName, kMin, kMax, kBucketCount,
HistogramBase::kUmaTargetedHistogramFlag));
}
TEST_F(SingleSampleMetricsTest, MultipleMetricsAreDistinct) {
SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get();
HistogramTester tester;
std::unique_ptr<SingleSampleMetric> metric =
factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
std::unique_ptr<SingleSampleMetric> metric2 =
factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
const char kMetricName2[] = "Single.Sample.Metric.2";
std::unique_ptr<SingleSampleMetric> metric3 =
factory->CreateCustomCountsMetric(kMetricName2, kMin, kMax, kBucketCount);
const HistogramBase::Sample kSample1 = 5;
metric->SetSample(kSample1);
metric2->SetSample(kSample1);
const HistogramBase::Sample kSample2 = 7;
metric3->SetSample(kSample2);
metric.reset();
tester.ExpectUniqueSample(kMetricName, kSample1, 1);
metric2.reset();
tester.ExpectUniqueSample(kMetricName, kSample1, 2);
metric3.reset();
tester.ExpectUniqueSample(kMetricName2, kSample2, 1);
}
} // namespace base
...@@ -91,8 +91,11 @@ static_library("metrics") { ...@@ -91,8 +91,11 @@ static_library("metrics") {
] ]
public_deps = [ public_deps = [
":single_sample_metrics",
"//components/metrics/proto", "//components/metrics/proto",
"//components/metrics/public/interfaces:single_sample_metrics_mojo_bindings",
] ]
deps = [ deps = [
":call_stack_profile_params", ":call_stack_profile_params",
"//base", "//base",
...@@ -247,6 +250,22 @@ if (!is_ios) { ...@@ -247,6 +250,22 @@ if (!is_ios) {
} }
} }
source_set("single_sample_metrics") {
sources = [
"single_sample_metrics.cc",
"single_sample_metrics.h",
"single_sample_metrics_factory_impl.cc",
"single_sample_metrics_factory_impl.h",
]
deps = [
"//components/metrics/public/interfaces:single_sample_metrics_mojo_bindings",
"//mojo/public/cpp/bindings",
"//services/service_manager/public/cpp",
"//services/service_manager/public/interfaces",
]
}
source_set("call_stack_profile_params") { source_set("call_stack_profile_params") {
sources = [ sources = [
"call_stack_profile_params.cc", "call_stack_profile_params.cc",
...@@ -334,6 +353,7 @@ source_set("unit_tests") { ...@@ -334,6 +353,7 @@ source_set("unit_tests") {
"persisted_logs_unittest.cc", "persisted_logs_unittest.cc",
"profiler/profiler_metrics_provider_unittest.cc", "profiler/profiler_metrics_provider_unittest.cc",
"profiler/tracking_synchronizer_unittest.cc", "profiler/tracking_synchronizer_unittest.cc",
"single_sample_metrics_factory_impl_unittest.cc",
"stability_metrics_helper_unittest.cc", "stability_metrics_helper_unittest.cc",
"stability_metrics_provider_unittest.cc", "stability_metrics_provider_unittest.cc",
"ui/screen_info_metrics_provider_unittest.cc", "ui/screen_info_metrics_provider_unittest.cc",
......
...@@ -24,3 +24,9 @@ mojom("call_stack_mojo_test_bindings") { ...@@ -24,3 +24,9 @@ mojom("call_stack_mojo_test_bindings") {
"//mojo/common:common_custom_types", "//mojo/common:common_custom_types",
] ]
} }
mojom("single_sample_metrics_mojo_bindings") {
sources = [
"single_sample_metrics.mojom",
]
}
// Copyright 2017 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.
module metrics.mojom;
// See components/metrics/single_sample_metrics_factory_impl.h for details.
interface SingleSampleMetricsProvider {
// Returns a SingleSampleMetric.
//
// A single sample metric only reports its sample once at destruction time.
// The sample may be changed prior to destruction using the SetSample() method
// as many times as desired.
//
// See base/metrics/histograms.h for parameter definitions. |request| is the
// returned histogram.
AcquireSingleSampleMetric(string histogram_name, int32 min, int32 max,
uint32 bucket_count, int32 flags,
SingleSampleMetric& request);
};
interface SingleSampleMetric {
SetSample(int32 sample);
};
// Copyright 2017 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 "components/metrics/single_sample_metrics.h"
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/metrics/single_sample_metrics.h"
#include "base/threading/thread_checker.h"
#include "components/metrics/single_sample_metrics_factory_impl.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace metrics {
namespace {
class MojoSingleSampleMetric : public mojom::SingleSampleMetric {
public:
MojoSingleSampleMetric(const std::string& histogram_name,
base::HistogramBase::Sample min,
base::HistogramBase::Sample max,
uint32_t bucket_count,
int32_t flags)
: metric_(histogram_name, min, max, bucket_count, flags) {}
~MojoSingleSampleMetric() override {}
private:
// mojom::SingleSampleMetric:
void SetSample(base::HistogramBase::Sample sample) override {
metric_.SetSample(sample);
}
base::DefaultSingleSampleMetric metric_;
DISALLOW_COPY_AND_ASSIGN(MojoSingleSampleMetric);
};
class MojoSingleSampleMetricsProvider
: public mojom::SingleSampleMetricsProvider {
public:
MojoSingleSampleMetricsProvider() {}
~MojoSingleSampleMetricsProvider() override {
DCHECK(thread_checker_.CalledOnValidThread());
}
private:
// mojom::SingleSampleMetricsProvider:
void AcquireSingleSampleMetric(
const std::string& histogram_name,
base::HistogramBase::Sample min,
base::HistogramBase::Sample max,
uint32_t bucket_count,
int32_t flags,
mojom::SingleSampleMetricRequest request) override {
DCHECK(thread_checker_.CalledOnValidThread());
mojo::MakeStrongBinding(base::MakeUnique<MojoSingleSampleMetric>(
histogram_name, min, max, bucket_count, flags),
std::move(request));
}
// Providers must be created, used on, and destroyed on the same thread.
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(MojoSingleSampleMetricsProvider);
};
} // namespace
// static
void InitializeSingleSampleMetricsFactory(CreateProviderCB create_provider_cb) {
base::SingleSampleMetricsFactory::SetFactory(
base::MakeUnique<SingleSampleMetricsFactoryImpl>(
std::move(create_provider_cb)));
}
// static
void CreateSingleSampleMetricsProvider(
const service_manager::BindSourceInfo& source_info,
mojom::SingleSampleMetricsProviderRequest request) {
mojo::MakeStrongBinding(base::MakeUnique<MojoSingleSampleMetricsProvider>(),
std::move(request));
}
} // namespace metrics
// Copyright 2017 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.
#ifndef COMPONENTS_METRICS_SINGLE_SAMPLE_METRICS_H_
#define COMPONENTS_METRICS_SINGLE_SAMPLE_METRICS_H_
#include "base/callback.h"
#include "components/metrics/public/interfaces/single_sample_metrics.mojom.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
namespace metrics {
using CreateProviderCB =
base::RepeatingCallback<void(mojom::SingleSampleMetricsProviderRequest)>;
// Initializes and sets the base::SingleSampleMetricsFactory for the current
// process. |create_provider_cb| is used to create provider instances per each
// thread that the factory is used on; this is necessary since the underlying
// providers must only be used on the same thread as construction.
//
// We use a callback here to avoid taking additional DEPS on content and a
// service_manager::Connector() for simplicity and to avoid the need for
// using the service test harness in metrics unittests.
//
// Typically this is called in the process where termination may occur without
// warning; e.g. perhaps a renderer process.
extern void InitializeSingleSampleMetricsFactory(
CreateProviderCB create_provider_cb);
// Creates a mojom::SingleSampleMetricsProvider capable of vending single sample
// metrics attached to a mojo pipe.
//
// Typically this is given to a service_manager::BinderRegistry in the process
// that has a deterministic shutdown path and which serves as a stable endpoint
// for the factory created by the above initialize method in another process.
extern void CreateSingleSampleMetricsProvider(
const service_manager::BindSourceInfo& source_info,
mojom::SingleSampleMetricsProviderRequest request);
} // namespace metrics
#endif // COMPONENTS_METRICS_SINGLE_SAMPLE_METRICS_H_
// Copyright 2017 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 "components/metrics/single_sample_metrics_factory_impl.h"
#include "base/threading/thread_checker.h"
namespace metrics {
namespace {
class SingleSampleMetricImpl : public base::SingleSampleMetric {
public:
SingleSampleMetricImpl(mojom::SingleSampleMetricPtr metric)
: metric_(std::move(metric)) {}
~SingleSampleMetricImpl() override {
DCHECK(thread_checker_.CalledOnValidThread());
}
void SetSample(base::HistogramBase::Sample sample) override {
DCHECK(thread_checker_.CalledOnValidThread());
metric_->SetSample(sample);
}
private:
base::ThreadChecker thread_checker_;
mojom::SingleSampleMetricPtr metric_;
DISALLOW_COPY_AND_ASSIGN(SingleSampleMetricImpl);
};
} // namespace
SingleSampleMetricsFactoryImpl::SingleSampleMetricsFactoryImpl(
CreateProviderCB create_provider_cb)
: create_provider_cb_(std::move(create_provider_cb)) {}
SingleSampleMetricsFactoryImpl::~SingleSampleMetricsFactoryImpl() {}
std::unique_ptr<base::SingleSampleMetric>
SingleSampleMetricsFactoryImpl::CreateCustomCountsMetric(
const std::string& histogram_name,
base::HistogramBase::Sample min,
base::HistogramBase::Sample max,
uint32_t bucket_count) {
return CreateMetric(histogram_name, min, max, bucket_count,
base::HistogramBase::kUmaTargetedHistogramFlag);
}
void SingleSampleMetricsFactoryImpl::DestroyProviderForTesting() {
if (auto* provider = provider_tls_.Get())
delete provider;
provider_tls_.Set(nullptr);
}
std::unique_ptr<base::SingleSampleMetric>
SingleSampleMetricsFactoryImpl::CreateMetric(const std::string& histogram_name,
base::HistogramBase::Sample min,
base::HistogramBase::Sample max,
uint32_t bucket_count,
int32_t flags) {
mojom::SingleSampleMetricPtr metric;
GetProvider()->AcquireSingleSampleMetric(
histogram_name, min, max, bucket_count, flags,
mojo::MakeRequest<mojom::SingleSampleMetric>(&metric));
return base::MakeUnique<SingleSampleMetricImpl>(std::move(metric));
}
mojom::SingleSampleMetricsProvider*
SingleSampleMetricsFactoryImpl::GetProvider() {
// Check the current TLS slot to see if we have created a provider already for
// this thread.
if (auto* provider = provider_tls_.Get())
return provider->get();
// If not, create a new one which will persist until process shutdown and put
// it in the TLS slot for the current thread.
mojom::SingleSampleMetricsProviderPtr* provider =
new mojom::SingleSampleMetricsProviderPtr();
provider_tls_.Set(provider);
// Start the provider connection and return it; it won't be fully connected
// until later, but mojo will buffer all calls prior to completion.
create_provider_cb_.Run(mojo::MakeRequest(provider));
return provider->get();
}
} // namespace metrics
// Copyright 2017 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.
#ifndef COMPONENTS_METRICS_SINGLE_VALUE_HISTOGRAM_FACTORY_IMPL_H_
#define COMPONENTS_METRICS_SINGLE_VALUE_HISTOGRAM_FACTORY_IMPL_H_
#include <string>
#include "base/metrics/single_sample_metrics.h"
#include "base/threading/thread_local.h"
#include "components/metrics/public/interfaces/single_sample_metrics.mojom.h"
#include "components/metrics/single_sample_metrics.h"
namespace metrics {
// SingleSampleMetricsFactory implementation for creating SingleSampleMetric
// instances that communicate over mojo to instances in another process.
//
// Persistance outside of the current process allows these metrics to record a
// sample even in the event of sudden process termination. As an example, this
// is useful for garbage collected objects which may never get a chance to run
// their destructors in the event of a fast shutdown event (process kill).
class SingleSampleMetricsFactoryImpl : public base::SingleSampleMetricsFactory {
public:
// Constructs a factory capable of vending single sample metrics from any
// thread. |create_provider_cb| will be called from arbitrary threads to
// create providers as necessary; the callback must handle thread safety.
//
// We use a callback here to avoid taking additional DEPS on content and a
// service_manager::Connector() for simplicitly and to avoid the need for
// using the service test harness just for instantiating this class.
explicit SingleSampleMetricsFactoryImpl(CreateProviderCB create_provider_cb);
~SingleSampleMetricsFactoryImpl() override;
// base::SingleSampleMetricsFactory:
std::unique_ptr<base::SingleSampleMetric> CreateCustomCountsMetric(
const std::string& histogram_name,
base::HistogramBase::Sample min,
base::HistogramBase::Sample max,
uint32_t bucket_count) override;
// Providers live forever in production, but tests should be kind and clean up
// after themselves to avoid tests trampling on one another. Destroys the
// provider in the TLS slot for the calling thread.
void DestroyProviderForTesting();
private:
// Creates a single sample metric.
std::unique_ptr<base::SingleSampleMetric> CreateMetric(
const std::string& histogram_name,
base::HistogramBase::Sample min,
base::HistogramBase::Sample max,
uint32_t bucket_count,
int32_t flags);
// Gets the SingleSampleMetricsProvider for the current thread. If none
// exists, then a new instance is created and set in the TLS slot.
mojom::SingleSampleMetricsProvider* GetProvider();
CreateProviderCB create_provider_cb_;
// Per thread storage slot for the mojo provider.
base::ThreadLocalPointer<mojom::SingleSampleMetricsProviderPtr> provider_tls_;
DISALLOW_COPY_AND_ASSIGN(SingleSampleMetricsFactoryImpl);
};
} // namespace metrics
#endif // COMPONENTS_METRICS_SINGLE_VALUE_HISTOGRAM_FACTORY_IMPL_H_
// Copyright 2017 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 "components/metrics/single_sample_metrics_factory_impl.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.h"
#include "base/test/histogram_tester.h"
#include "base/threading/thread.h"
#include "components/metrics/single_sample_metrics.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
const base::HistogramBase::Sample kMin = 1;
const base::HistogramBase::Sample kMax = 10;
const uint32_t kBucketCount = 10;
const char kMetricName[] = "Single.Sample.Metric";
class SingleSampleMetricsFactoryImplTest : public testing::Test {
public:
SingleSampleMetricsFactoryImplTest() : thread_("TestThread") {
InitializeSingleSampleMetricsFactory(
base::BindRepeating(&SingleSampleMetricsFactoryImplTest::CreateProvider,
base::Unretained(this)));
factory_ = static_cast<SingleSampleMetricsFactoryImpl*>(
base::SingleSampleMetricsFactory::Get());
}
~SingleSampleMetricsFactoryImplTest() override {
factory_->DestroyProviderForTesting();
if (thread_.IsRunning()) {
thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&SingleSampleMetricsFactoryImpl::DestroyProviderForTesting,
base::Unretained(factory_)));
thread_.Stop();
}
base::SingleSampleMetricsFactory::DeleteFactoryForTesting();
}
protected:
void StartThread() { ASSERT_TRUE(thread_.Start()); }
void CreateProvider(mojom::SingleSampleMetricsProviderRequest request) {
CreateSingleSampleMetricsProvider(service_manager::BindSourceInfo(),
std::move(request));
provider_count_++;
}
std::unique_ptr<base::SingleSampleMetric> CreateMetricOnThread() {
std::unique_ptr<base::SingleSampleMetric> metric;
base::RunLoop run_loop;
thread_.task_runner()->PostTaskAndReply(
FROM_HERE,
base::Bind(&SingleSampleMetricsFactoryImplTest::CreateAndStoreMetric,
base::Unretained(this), &metric),
run_loop.QuitClosure());
run_loop.Run();
return metric;
}
void CreateAndStoreMetric(std::unique_ptr<base::SingleSampleMetric>* metric) {
*metric = factory_->CreateCustomCountsMetric(kMetricName, kMin, kMax,
kBucketCount);
}
base::MessageLoop message_looqp_;
SingleSampleMetricsFactoryImpl* factory_;
base::Thread thread_;
size_t provider_count_ = 0;
private:
DISALLOW_COPY_AND_ASSIGN(SingleSampleMetricsFactoryImplTest);
};
} // namespace
TEST_F(SingleSampleMetricsFactoryImplTest, SingleProvider) {
std::unique_ptr<base::SingleSampleMetric> metric1 =
factory_->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
std::unique_ptr<base::SingleSampleMetric> metric2 =
factory_->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
// Verify that only a single provider is created for multiple metrics.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, provider_count_);
}
TEST_F(SingleSampleMetricsFactoryImplTest, DoesNothing) {
base::HistogramTester tester;
std::unique_ptr<base::SingleSampleMetric> metric =
factory_->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
metric.reset();
// Verify that no sample is recorded if SetSample() is never called.
base::RunLoop().RunUntilIdle();
tester.ExpectTotalCount(kMetricName, 0);
}
TEST_F(SingleSampleMetricsFactoryImplTest, DefaultSingleSampleMetricWithValue) {
base::HistogramTester tester;
std::unique_ptr<base::SingleSampleMetric> metric =
factory_->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
const base::HistogramBase::Sample kLastSample = 9;
metric->SetSample(1);
metric->SetSample(3);
metric->SetSample(5);
metric->SetSample(kLastSample);
metric.reset();
// Verify only the last sample sent to SetSample() is recorded.
base::RunLoop().RunUntilIdle();
tester.ExpectUniqueSample(kMetricName, kLastSample, 1);
// Verify construction implicitly by requesting a histogram with the same
// parameters; this test relies on the fact that histogram objects are unique
// per name. Different parameters will result in a nullptr being returned.
EXPECT_FALSE(base::Histogram::FactoryGet(kMetricName, 1, 3, 3,
base::HistogramBase::kNoFlags));
EXPECT_TRUE(base::Histogram::FactoryGet(
kMetricName, kMin, kMax, kBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
}
TEST_F(SingleSampleMetricsFactoryImplTest, MultithreadedMetrics) {
base::HistogramTester tester;
std::unique_ptr<base::SingleSampleMetric> metric =
factory_->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount);
EXPECT_EQ(1u, provider_count_);
StartThread();
std::unique_ptr<base::SingleSampleMetric> threaded_metric =
CreateMetricOnThread();
ASSERT_TRUE(threaded_metric);
// A second provider should be created to handle requests on our new thread.
EXPECT_EQ(2u, provider_count_);
// Calls from the wrong thread should DCHECK.
EXPECT_DCHECK_DEATH(threaded_metric->SetSample(5));
EXPECT_DCHECK_DEATH(threaded_metric.reset());
// Test that samples are set on each thread correctly.
const base::HistogramBase::Sample kSample = 7;
{
metric->SetSample(kSample);
base::RunLoop run_loop;
thread_.task_runner()->PostTaskAndReply(
FROM_HERE,
base::Bind(&base::SingleSampleMetric::SetSample,
base::Unretained(threaded_metric.get()), kSample),
run_loop.QuitClosure());
run_loop.Run();
}
// Release metrics and cycle threads to ensure destruction completes.
{
thread_.task_runner()->DeleteSoon(FROM_HERE, threaded_metric.release());
base::RunLoop run_loop;
thread_.task_runner()->PostTaskAndReply(
FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
run_loop.Run();
}
metric.reset();
base::RunLoop().RunUntilIdle();
tester.ExpectUniqueSample(kMetricName, kSample, 2);
}
} // namespace metrics
...@@ -14,6 +14,7 @@ source_set("test_support") { ...@@ -14,6 +14,7 @@ source_set("test_support") {
"//components/content_settings/core/common", "//components/content_settings/core/common",
"//components/gcm_driver:gcm_driver", "//components/gcm_driver:gcm_driver",
"//components/signin/core/browser", "//components/signin/core/browser",
"//mojo/edk/system",
"//net", "//net",
"//testing/gtest", "//testing/gtest",
"//ui/base", "//ui/base",
......
...@@ -8,3 +8,9 @@ include_rules = [ ...@@ -8,3 +8,9 @@ include_rules = [
"+net", "+net",
"+ui", "+ui",
] ]
specific_include_rules = {
"components_test_suite\.cc": [
"+mojo/edk/embedder",
],
}
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/test/test_suite.h" #include "base/test/test_suite.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/content_settings/core/common/content_settings_pattern.h" #include "components/content_settings/core/common/content_settings_pattern.h"
#include "mojo/edk/embedder/embedder.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_paths.h"
...@@ -53,6 +54,8 @@ class ComponentsTestSuite : public base::TestSuite { ...@@ -53,6 +54,8 @@ class ComponentsTestSuite : public base::TestSuite {
void Initialize() override { void Initialize() override {
base::TestSuite::Initialize(); base::TestSuite::Initialize();
mojo::edk::Init();
// Initialize the histograms subsystem, so that any histograms hit in tests // Initialize the histograms subsystem, so that any histograms hit in tests
// are correctly registered with the statistics recorder and can be queried // are correctly registered with the statistics recorder and can be queried
// by tests. // by tests.
......
...@@ -49,6 +49,7 @@ source_set("browser") { ...@@ -49,6 +49,7 @@ source_set("browser") {
"//components/filesystem:lib", "//components/filesystem:lib",
"//components/leveldb:lib", "//components/leveldb:lib",
"//components/link_header_util", "//components/link_header_util",
"//components/metrics",
"//components/mime_util", "//components/mime_util",
"//components/payments/mojom:mojom_payment_app", "//components/payments/mojom:mojom_payment_app",
"//components/rappor", "//components/rappor",
......
...@@ -7,6 +7,7 @@ include_rules = [ ...@@ -7,6 +7,7 @@ include_rules = [
"+components/filesystem", "+components/filesystem",
"+components/leveldb", "+components/leveldb",
"+components/link_header_util", "+components/link_header_util",
"+components/metrics",
"+components/mime_util", "+components/mime_util",
"+components/payments", "+components/payments",
"+components/profile_service", "+components/profile_service",
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include "cc/base/switches.h" #include "cc/base/switches.h"
#include "cc/output/buffer_to_texture_target_map.h" #include "cc/output/buffer_to_texture_target_map.h"
#include "components/discardable_memory/service/discardable_shared_memory_manager.h" #include "components/discardable_memory/service/discardable_shared_memory_manager.h"
#include "components/metrics/single_sample_metrics.h"
#include "components/tracing/common/tracing_switches.h" #include "components/tracing/common/tracing_switches.h"
#include "content/browser/appcache/appcache_dispatcher_host.h" #include "content/browser/appcache/appcache_dispatcher_host.h"
#include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/appcache/chrome_appcache_service.h"
...@@ -1322,6 +1323,9 @@ void RenderProcessHostImpl::RegisterMojoInterfaces() { ...@@ -1322,6 +1323,9 @@ void RenderProcessHostImpl::RegisterMojoInterfaces() {
base::Bind(&VideoCaptureHost::Create, base::Bind(&VideoCaptureHost::Create,
BrowserMainLoop::GetInstance()->media_stream_manager())); BrowserMainLoop::GetInstance()->media_stream_manager()));
registry->AddInterface(
base::Bind(&metrics::CreateSingleSampleMetricsProvider));
if (base::FeatureList::IsEnabled(features::kOffMainThreadFetch)) { if (base::FeatureList::IsEnabled(features::kOffMainThreadFetch)) {
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context( scoped_refptr<ServiceWorkerContextWrapper> service_worker_context(
static_cast<ServiceWorkerContextWrapper*>( static_cast<ServiceWorkerContextWrapper*>(
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
"media::mojom::ImageCapture", "media::mojom::ImageCapture",
"memory_coordinator::mojom::MemoryCoordinatorHandle", "memory_coordinator::mojom::MemoryCoordinatorHandle",
"memory_instrumentation::mojom::Coordinator", "memory_instrumentation::mojom::Coordinator",
"metrics::mojom::SingleSampleMetricsProvider",
"payments::mojom::PaymentManager", "payments::mojom::PaymentManager",
"shape_detection::mojom::BarcodeDetection", "shape_detection::mojom::BarcodeDetection",
"shape_detection::mojom::FaceDetectionProvider", "shape_detection::mojom::FaceDetectionProvider",
......
...@@ -434,6 +434,7 @@ target(link_target_type, "renderer") { ...@@ -434,6 +434,7 @@ target(link_target_type, "renderer") {
"//cc/surfaces:surface_id", "//cc/surfaces:surface_id",
"//cc/surfaces:surfaces", "//cc/surfaces:surfaces",
"//components/discardable_memory/client", "//components/discardable_memory/client",
"//components/metrics",
"//components/payments/mojom:mojom_payment_app", "//components/payments/mojom:mojom_payment_app",
"//components/url_formatter", "//components/url_formatter",
"//components/variations", "//components/variations",
......
...@@ -2,6 +2,7 @@ include_rules = [ ...@@ -2,6 +2,7 @@ include_rules = [
# Allow inclusion of specific components that we depend on. # Allow inclusion of specific components that we depend on.
# See comment in content/DEPS for which components are allowed. # See comment in content/DEPS for which components are allowed.
"+components/discardable_memory/client", "+components/discardable_memory/client",
"+components/metrics",
"+components/payments", "+components/payments",
"+components/scheduler", "+components/scheduler",
"+components/url_formatter", "+components/url_formatter",
......
...@@ -51,6 +51,8 @@ ...@@ -51,6 +51,8 @@
#include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_settings.h" #include "cc/trees/layer_tree_settings.h"
#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h" #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
#include "components/metrics/public/interfaces/single_sample_metrics.mojom.h"
#include "components/metrics/single_sample_metrics.h"
#include "content/child/appcache/appcache_dispatcher.h" #include "content/child/appcache/appcache_dispatcher.h"
#include "content/child/appcache/appcache_frontend_impl.h" #include "content/child/appcache/appcache_frontend_impl.h"
#include "content/child/blob_storage/blob_message_filter.h" #include "content/child/blob_storage/blob_message_filter.h"
...@@ -393,6 +395,23 @@ bool IsRunningInMash() { ...@@ -393,6 +395,23 @@ bool IsRunningInMash() {
return cmdline->HasSwitch(switches::kIsRunningInMash); return cmdline->HasSwitch(switches::kIsRunningInMash);
} }
// Hook that allows single-sample metric code from //components/metrics to
// connect from the renderer process to the browser process.
void CreateSingleSampleMetricsProvider(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
service_manager::Connector* connector,
metrics::mojom::SingleSampleMetricsProviderRequest request) {
if (task_runner->BelongsToCurrentThread()) {
connector->BindInterface(mojom::kBrowserServiceName, std::move(request));
return;
}
task_runner->PostTask(
FROM_HERE,
base::Bind(&CreateSingleSampleMetricsProvider, std::move(task_runner),
connector, base::Passed(&request)));
}
} // namespace } // namespace
// For measuring memory usage after each task. Behind a command line flag. // For measuring memory usage after each task. Behind a command line flag.
...@@ -614,6 +633,10 @@ void RenderThreadImpl::Init( ...@@ -614,6 +633,10 @@ void RenderThreadImpl::Init(
// Register this object as the main thread. // Register this object as the main thread.
ChildProcess::current()->set_main_thread(this); ChildProcess::current()->set_main_thread(this);
metrics::InitializeSingleSampleMetricsFactory(
base::BindRepeating(&CreateSingleSampleMetricsProvider,
message_loop()->task_runner(), GetConnector()));
gpu_ = ui::Gpu::Create( gpu_ = ui::Gpu::Create(
GetConnector(), GetConnector(),
IsRunningInMash() ? ui::mojom::kServiceName : mojom::kBrowserServiceName, IsRunningInMash() ? ui::mojom::kServiceName : mojom::kBrowserServiceName,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment