summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorbcwhite <bcwhite@chromium.org>2016-02-01 13:07:56 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-01 21:10:17 +0000
commit5cb99ebcf2cf1450e7a68e7d7fd3d11ce0549175 (patch)
tree8b8c7a106a6f446d0ccaa49275bdae4fa1fa2cba /base
parent73fb42c1d2867e2dd3756b1fce0f45d1fa9bee44 (diff)
downloadchromium_src-5cb99ebcf2cf1450e7a68e7d7fd3d11ce0549175.zip
chromium_src-5cb99ebcf2cf1450e7a68e7d7fd3d11ce0549175.tar.gz
chromium_src-5cb99ebcf2cf1450e7a68e7d7fd3d11ce0549175.tar.bz2
Support "shared" histograms between processes.
Histograms have tradionally been held on the local heap and passed between processes using RPCs or simply lost when that process stops. Shared histograms are read directly by another process. https://docs.google.com/document/d/1YvBXzi745UDjiFVLjP8IWUMFgYL9gDb2J4xuwcv-c7o/edit?usp=sharing BUG=546019 TBR=mmenke,nasko,thakis for base/*.{gn,gypi}, components/cronet/*, and content/child/* Review URL: https://codereview.chromium.org/1425533011 Cr-Commit-Position: refs/heads/master@{#372760}
Diffstat (limited to 'base')
-rw-r--r--base/BUILD.gn2
-rw-r--r--base/base.gypi2
-rw-r--r--base/metrics/histogram.cc434
-rw-r--r--base/metrics/histogram.h80
-rw-r--r--base/metrics/histogram_base.h8
-rw-r--r--base/metrics/histogram_delta_serialization.cc5
-rw-r--r--base/metrics/histogram_delta_serialization.h9
-rw-r--r--base/metrics/histogram_delta_serialization_unittest.cc4
-rw-r--r--base/metrics/histogram_persistence.cc422
-rw-r--r--base/metrics/histogram_persistence.h89
-rw-r--r--base/metrics/histogram_snapshot_manager.cc14
-rw-r--r--base/metrics/histogram_snapshot_manager.h13
-rw-r--r--base/metrics/histogram_snapshot_manager_unittest.cc7
-rw-r--r--base/metrics/histogram_unittest.cc124
-rw-r--r--base/metrics/sample_vector.h1
-rw-r--r--base/metrics/statistics_recorder.cc50
-rw-r--r--base/metrics/statistics_recorder.h38
-rw-r--r--base/metrics/statistics_recorder_unittest.cc26
18 files changed, 1185 insertions, 143 deletions
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 60821ee..7e5c458 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -501,6 +501,8 @@ component("base") {
"metrics/histogram_delta_serialization.h",
"metrics/histogram_flattener.h",
"metrics/histogram_macros.h",
+ "metrics/histogram_persistence.cc",
+ "metrics/histogram_persistence.h",
"metrics/histogram_samples.cc",
"metrics/histogram_samples.h",
"metrics/histogram_snapshot_manager.cc",
diff --git a/base/base.gypi b/base/base.gypi
index 6eb8163..db52b13 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -402,6 +402,8 @@
'metrics/histogram_delta_serialization.h',
'metrics/histogram_flattener.h',
'metrics/histogram_macros.h',
+ 'metrics/histogram_persistence.cc',
+ 'metrics/histogram_persistence.h',
'metrics/histogram_samples.cc',
'metrics/histogram_samples.h',
'metrics/histogram_snapshot_manager.cc',
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 62c2bc8..a1a6ac7 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -19,7 +19,9 @@
#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_persistence.h"
#include "base/metrics/metrics_hashes.h"
+#include "base/metrics/persistent_memory_allocator.h"
#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
@@ -84,45 +86,160 @@ typedef HistogramBase::Sample Sample;
// static
const size_t Histogram::kBucketCount_MAX = 16384u;
-HistogramBase* Histogram::FactoryGet(const std::string& name,
- Sample minimum,
- Sample maximum,
- size_t bucket_count,
- int32_t flags) {
- bool valid_arguments =
- InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
- DCHECK(valid_arguments);
+class Histogram::Factory {
+ public:
+ Factory(const std::string& name,
+ HistogramBase::Sample minimum,
+ HistogramBase::Sample maximum,
+ size_t bucket_count,
+ int32_t flags)
+ : Factory(name, HISTOGRAM, minimum, maximum, bucket_count, flags) {}
+
+ // Create histogram based on construction parameters. Caller takes
+ // ownership of the returned object.
+ HistogramBase* Build();
+
+ protected:
+ Factory(const std::string& name,
+ HistogramType histogram_type,
+ HistogramBase::Sample minimum,
+ HistogramBase::Sample maximum,
+ size_t bucket_count,
+ int32_t flags)
+ : name_(name),
+ histogram_type_(histogram_type),
+ minimum_(minimum),
+ maximum_(maximum),
+ bucket_count_(bucket_count),
+ flags_(flags) {}
+
+ // Create a BucketRanges structure appropriate for this histogram.
+ virtual BucketRanges* CreateRanges() {
+ BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
+ Histogram::InitializeBucketRanges(minimum_, maximum_, ranges);
+ return ranges;
+ }
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
+ // Allocate the correct Histogram object off the heap (in case persistent
+ // memory is not available).
+ virtual HistogramBase* HeapAlloc(const BucketRanges* ranges) {
+ return new Histogram(name_, minimum_, maximum_, ranges);
+ }
+
+ // Perform any required datafill on the just-created histogram. If
+ // overridden, be sure to call the "super" version.
+ virtual void FillHistogram(HistogramBase* histogram) {
+ histogram->SetFlags(flags_);
+ }
+
+ // These values are protected (instead of private) because they need to
+ // be accessible to methods of sub-classes in order to avoid passing
+ // unnecessary parameters everywhere.
+ const std::string& name_;
+ const HistogramType histogram_type_;
+ HistogramBase::Sample minimum_;
+ HistogramBase::Sample maximum_;
+ size_t bucket_count_;
+ int32_t flags_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Factory);
+};
+
+HistogramBase* Histogram::Factory::Build() {
+ // Import histograms from known persistent storage. Histograms could have
+ // been added by other processes and they must be fetched and recognized
+ // locally in order to be found by FindHistograms() below. If the persistent
+ // memory segment is not shared between processes, this call does nothing.
+ ImportPersistentHistograms();
+
+ HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_);
if (!histogram) {
// To avoid racy destruction at shutdown, the following will be leaked.
- BucketRanges* ranges = new BucketRanges(bucket_count + 1);
- InitializeBucketRanges(minimum, maximum, ranges);
+ const BucketRanges* created_ranges = CreateRanges();
const BucketRanges* registered_ranges =
- StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+ StatisticsRecorder::RegisterOrDeleteDuplicateRanges(created_ranges);
+
+ // In most cases, the bucket-count, minimum, and maximum values are known
+ // when the code is written and so are passed in explicitly. In other
+ // cases (such as with a CustomHistogram), they are calculated dynamically
+ // at run-time. In the latter case, those ctor parameters are zero and
+ // the results extracted from the result of CreateRanges().
+ if (bucket_count_ == 0) {
+ bucket_count_ = registered_ranges->bucket_count();
+ minimum_ = registered_ranges->range(1);
+ maximum_ = registered_ranges->range(bucket_count_ - 1);
+ }
- Histogram* tentative_histogram =
- new Histogram(name, minimum, maximum, registered_ranges);
+ // Try to create the histogram using a "persistent" allocator. As of
+ // 2015-01-14, the availability of such is controlled by a base::Feature
+ // that is off by default. If the allocator doesn't exist or if
+ // allocating from it fails, code below will allocate the histogram from
+ // the process heap.
+ PersistentMemoryAllocator::Reference histogram_ref = 0;
+ HistogramBase* tentative_histogram = nullptr;
+ PersistentMemoryAllocator* allocator =
+ GetPersistentHistogramMemoryAllocator();
+ if (allocator) {
+ flags_ |= HistogramBase::kIsPersistent;
+ tentative_histogram = AllocatePersistentHistogram(
+ allocator,
+ histogram_type_,
+ name_,
+ minimum_,
+ maximum_,
+ registered_ranges,
+ flags_,
+ &histogram_ref);
+ }
+
+ // Handle the case where no persistent allocator is present or the
+ // persistent allocation fails (perhaps because it is full).
+ if (!tentative_histogram) {
+ DCHECK(!histogram_ref); // Should never have been set.
+ DCHECK(!allocator); // Shouldn't have failed.
+ flags_ &= ~HistogramBase::kIsPersistent;
+ tentative_histogram = HeapAlloc(registered_ranges);
+ }
- tentative_histogram->SetFlags(flags);
+ FillHistogram(tentative_histogram);
histogram =
StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+
+ // Persistent histograms need some follow-up processing.
+ if (histogram_ref) {
+ FinalizePersistentHistogram(histogram_ref,
+ histogram == tentative_histogram);
+ }
}
- DCHECK_EQ(HISTOGRAM, histogram->GetHistogramType());
- if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) {
+ DCHECK_EQ(histogram_type_, histogram->GetHistogramType());
+ if (bucket_count_ != 0 &&
+ !histogram->HasConstructionArguments(minimum_, maximum_, bucket_count_)) {
// The construction arguments do not match the existing histogram. This can
// come about if an extension updates in the middle of a chrome run and has
// changed one of them, or simply by bad code within Chrome itself. We
// return NULL here with the expectation that bad code in Chrome will crash
// on dereference, but extension/Pepper APIs will guard against NULL and not
// crash.
- DLOG(ERROR) << "Histogram " << name << " has bad construction arguments";
- return NULL;
+ DLOG(ERROR) << "Histogram " << name_ << " has bad construction arguments";
+ return nullptr;
}
return histogram;
}
+HistogramBase* Histogram::FactoryGet(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ size_t bucket_count,
+ int32_t flags) {
+ bool valid_arguments =
+ InspectConstructionArguments(name, &minimum, &maximum, &bucket_count);
+ DCHECK(valid_arguments);
+
+ return Factory(name, minimum, maximum, bucket_count, flags).Build();
+}
+
HistogramBase* Histogram::FactoryTimeGet(const std::string& name,
TimeDelta minimum,
TimeDelta maximum,
@@ -150,6 +267,17 @@ HistogramBase* Histogram::FactoryTimeGet(const char* name,
flags);
}
+HistogramBase* Histogram::PersistentGet(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta) {
+ return new Histogram(name, minimum, maximum, ranges, counts, counts_size,
+ meta);
+}
+
// Calculate what range of values are held in each bucket.
// We have to be careful that we don't pick a ratio between starting points in
// consecutive buckets that is sooo small, that the integer bounds are the same
@@ -344,6 +472,23 @@ Histogram::Histogram(const std::string& name,
samples_.reset(new SampleVector(HashMetricName(name), ranges));
}
+Histogram::Histogram(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta)
+ : HistogramBase(name),
+ bucket_ranges_(ranges),
+ declared_min_(minimum),
+ declared_max_(maximum) {
+ if (ranges) {
+ samples_.reset(new SampleVector(HashMetricName(name),
+ counts, counts_size, meta, ranges));
+ }
+}
+
Histogram::~Histogram() {
}
@@ -545,6 +690,48 @@ void Histogram::GetCountAndBucketData(Count* count,
// buckets.
//------------------------------------------------------------------------------
+class LinearHistogram::Factory : public Histogram::Factory {
+ public:
+ Factory(const std::string& name,
+ HistogramBase::Sample minimum,
+ HistogramBase::Sample maximum,
+ size_t bucket_count,
+ int32_t flags,
+ const DescriptionPair* descriptions)
+ : Histogram::Factory(name, LINEAR_HISTOGRAM, minimum, maximum,
+ bucket_count, flags) {
+ descriptions_ = descriptions;
+ }
+
+ protected:
+ BucketRanges* CreateRanges() override {
+ BucketRanges* ranges = new BucketRanges(bucket_count_ + 1);
+ LinearHistogram::InitializeBucketRanges(minimum_, maximum_, ranges);
+ return ranges;
+ }
+
+ HistogramBase* HeapAlloc(const BucketRanges* ranges) override {
+ return new LinearHistogram(name_, minimum_, maximum_, ranges);
+ }
+
+ void FillHistogram(HistogramBase* base_histogram) override {
+ Histogram::Factory::FillHistogram(base_histogram);
+ LinearHistogram* histogram = static_cast<LinearHistogram*>(base_histogram);
+ // Set range descriptions.
+ if (descriptions_) {
+ for (int i = 0; descriptions_[i].description; ++i) {
+ histogram->bucket_description_[descriptions_[i].sample] =
+ descriptions_[i].description;
+ }
+ }
+ }
+
+ private:
+ const DescriptionPair* descriptions_;
+
+ DISALLOW_COPY_AND_ASSIGN(Factory);
+};
+
LinearHistogram::~LinearHistogram() {}
HistogramBase* LinearHistogram::FactoryGet(const std::string& name,
@@ -583,6 +770,18 @@ HistogramBase* LinearHistogram::FactoryTimeGet(const char* name,
flags);
}
+HistogramBase* LinearHistogram::PersistentGet(
+ const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta) {
+ return new LinearHistogram(name, minimum, maximum, ranges, counts,
+ counts_size, meta);
+}
+
HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
const std::string& name,
Sample minimum,
@@ -594,42 +793,8 @@ HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
name, &minimum, &maximum, &bucket_count);
DCHECK(valid_arguments);
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (!histogram) {
- // To avoid racy destruction at shutdown, the following will be leaked.
- BucketRanges* ranges = new BucketRanges(bucket_count + 1);
- InitializeBucketRanges(minimum, maximum, ranges);
- const BucketRanges* registered_ranges =
- StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
-
- LinearHistogram* tentative_histogram =
- new LinearHistogram(name, minimum, maximum, registered_ranges);
-
- // Set range descriptions.
- if (descriptions) {
- for (int i = 0; descriptions[i].description; ++i) {
- tentative_histogram->bucket_description_[descriptions[i].sample] =
- descriptions[i].description;
- }
- }
-
- tentative_histogram->SetFlags(flags);
- histogram =
- StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
- }
-
- DCHECK_EQ(LINEAR_HISTOGRAM, histogram->GetHistogramType());
- if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) {
- // The construction arguments do not match the existing histogram. This can
- // come about if an extension updates in the middle of a chrome run and has
- // changed one of them, or simply by bad code within Chrome itself. We
- // return NULL here with the expectation that bad code in Chrome will crash
- // on dereference, but extension/Pepper APIs will guard against NULL and not
- // crash.
- DLOG(ERROR) << "Histogram " << name << " has bad construction arguments";
- return NULL;
- }
- return histogram;
+ return Factory(name, minimum, maximum, bucket_count, flags, descriptions)
+ .Build();
}
HistogramType LinearHistogram::GetHistogramType() const {
@@ -643,6 +808,21 @@ LinearHistogram::LinearHistogram(const std::string& name,
: Histogram(name, minimum, maximum, ranges) {
}
+LinearHistogram::LinearHistogram(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta)
+ : Histogram(name,
+ minimum,
+ maximum,
+ ranges,
+ counts,
+ counts_size,
+ meta) {}
+
double LinearHistogram::GetBucketSize(Count current, size_t i) const {
DCHECK_GT(ranges(i + 1), ranges(i));
// Adjacent buckets with different widths would have "surprisingly" many (few)
@@ -706,32 +886,43 @@ HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) {
// This section provides implementation for BooleanHistogram.
//------------------------------------------------------------------------------
-HistogramBase* BooleanHistogram::FactoryGet(const std::string& name,
- int32_t flags) {
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (!histogram) {
- // To avoid racy destruction at shutdown, the following will be leaked.
- BucketRanges* ranges = new BucketRanges(4);
- LinearHistogram::InitializeBucketRanges(1, 2, ranges);
- const BucketRanges* registered_ranges =
- StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+class BooleanHistogram::Factory : public Histogram::Factory {
+ public:
+ Factory(const std::string& name, int32_t flags)
+ : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {}
- BooleanHistogram* tentative_histogram =
- new BooleanHistogram(name, registered_ranges);
+ protected:
+ BucketRanges* CreateRanges() override {
+ BucketRanges* ranges = new BucketRanges(3 + 1);
+ LinearHistogram::InitializeBucketRanges(1, 2, ranges);
+ return ranges;
+ }
- tentative_histogram->SetFlags(flags);
- histogram =
- StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
+ HistogramBase* HeapAlloc(const BucketRanges* ranges) override {
+ return new BooleanHistogram(name_, ranges);
}
- DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->GetHistogramType());
- return histogram;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Factory);
+};
+
+HistogramBase* BooleanHistogram::FactoryGet(const std::string& name,
+ int32_t flags) {
+ return Factory(name, flags).Build();
}
HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) {
return FactoryGet(std::string(name), flags);
}
+HistogramBase* BooleanHistogram::PersistentGet(
+ const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ HistogramSamples::Metadata* meta) {
+ return new BooleanHistogram(name, ranges, counts, meta);
+}
+
HistogramType BooleanHistogram::GetHistogramType() const {
return BOOLEAN_HISTOGRAM;
}
@@ -740,6 +931,12 @@ BooleanHistogram::BooleanHistogram(const std::string& name,
const BucketRanges* ranges)
: LinearHistogram(name, 1, 2, ranges) {}
+BooleanHistogram::BooleanHistogram(const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ HistogramSamples::Metadata* meta)
+ : LinearHistogram(name, 1, 2, ranges, counts, 2, meta) {}
+
HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) {
std::string histogram_name;
int flags;
@@ -766,30 +963,49 @@ HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) {
// CustomHistogram:
//------------------------------------------------------------------------------
+class CustomHistogram::Factory : public Histogram::Factory {
+ public:
+ Factory(const std::string& name,
+ const std::vector<Sample>* custom_ranges,
+ int32_t flags)
+ : Histogram::Factory(name, CUSTOM_HISTOGRAM, 0, 0, 0, flags) {
+ custom_ranges_ = custom_ranges;
+ }
+
+ protected:
+ BucketRanges* CreateRanges() override {
+ // Remove the duplicates in the custom ranges array.
+ std::vector<int> ranges = *custom_ranges_;
+ ranges.push_back(0); // Ensure we have a zero value.
+ ranges.push_back(HistogramBase::kSampleType_MAX);
+ std::sort(ranges.begin(), ranges.end());
+ ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
+
+ BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
+ for (size_t i = 0; i < ranges.size(); i++) {
+ bucket_ranges->set_range(i, ranges[i]);
+ }
+ bucket_ranges->ResetChecksum();
+ return bucket_ranges;
+ }
+
+ HistogramBase* HeapAlloc(const BucketRanges* ranges) override {
+ return new CustomHistogram(name_, ranges);
+ }
+
+ private:
+ const std::vector<Sample>* custom_ranges_;
+
+ DISALLOW_COPY_AND_ASSIGN(Factory);
+};
+
HistogramBase* CustomHistogram::FactoryGet(
const std::string& name,
const std::vector<Sample>& custom_ranges,
int32_t flags) {
CHECK(ValidateCustomRanges(custom_ranges));
- HistogramBase* histogram = StatisticsRecorder::FindHistogram(name);
- if (!histogram) {
- BucketRanges* ranges = CreateBucketRangesFromCustomRanges(custom_ranges);
- const BucketRanges* registered_ranges =
- StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
-
- // To avoid racy destruction at shutdown, the following will be leaked.
- CustomHistogram* tentative_histogram =
- new CustomHistogram(name, registered_ranges);
-
- tentative_histogram->SetFlags(flags);
-
- histogram =
- StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram);
- }
-
- DCHECK_EQ(histogram->GetHistogramType(), CUSTOM_HISTOGRAM);
- return histogram;
+ return Factory(name, &custom_ranges, flags).Build();
}
HistogramBase* CustomHistogram::FactoryGet(
@@ -799,6 +1015,15 @@ HistogramBase* CustomHistogram::FactoryGet(
return FactoryGet(std::string(name), custom_ranges, flags);
}
+HistogramBase* CustomHistogram::PersistentGet(
+ const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta) {
+ return new CustomHistogram(name, ranges, counts, counts_size, meta);
+}
+
HistogramType CustomHistogram::GetHistogramType() const {
return CUSTOM_HISTOGRAM;
}
@@ -825,6 +1050,19 @@ CustomHistogram::CustomHistogram(const std::string& name,
ranges->range(ranges->bucket_count() - 1),
ranges) {}
+CustomHistogram::CustomHistogram(const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta)
+ : Histogram(name,
+ ranges->range(1),
+ ranges->range(ranges->bucket_count() - 1),
+ ranges,
+ counts,
+ counts_size,
+ meta) {}
+
bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const {
if (!Histogram::SerializeInfoImpl(pickle))
return false;
@@ -887,22 +1125,4 @@ bool CustomHistogram::ValidateCustomRanges(
return has_valid_range;
}
-// static
-BucketRanges* CustomHistogram::CreateBucketRangesFromCustomRanges(
- const std::vector<Sample>& custom_ranges) {
- // Remove the duplicates in the custom ranges array.
- std::vector<int> ranges = custom_ranges;
- ranges.push_back(0); // Ensure we have a zero value.
- ranges.push_back(HistogramBase::kSampleType_MAX);
- std::sort(ranges.begin(), ranges.end());
- ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
-
- BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
- for (size_t i = 0; i < ranges.size(); i++) {
- bucket_ranges->set_range(i, ranges[i]);
- }
- bucket_ranges->ResetChecksum();
- return bucket_ranges;
-}
-
} // namespace base
diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h
index 93f704a..3681225 100644
--- a/base/metrics/histogram.h
+++ b/base/metrics/histogram.h
@@ -92,6 +92,7 @@ class BooleanHistogram;
class CustomHistogram;
class Histogram;
class LinearHistogram;
+class PersistentMemoryAllocator;
class Pickle;
class PickleIterator;
class SampleVector;
@@ -138,6 +139,15 @@ class BASE_EXPORT Histogram : public HistogramBase {
size_t bucket_count,
int32_t flags);
+ // Get a histogram using data in persistent storage.
+ static HistogramBase* PersistentGet(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta);
+
static void InitializeBucketRanges(Sample minimum,
Sample maximum,
BucketRanges* ranges);
@@ -193,6 +203,13 @@ class BASE_EXPORT Histogram : public HistogramBase {
void WriteAscii(std::string* output) const override;
protected:
+ // This class, defined entirely within the .cc file, contains all the
+ // common logic for building a Histogram and can be overridden by more
+ // specific types to alter details of how the creation is done. It is
+ // defined as an embedded class (rather than an anonymous one) so it
+ // can access the protected constructors.
+ class Factory;
+
// |ranges| should contain the underflow and overflow buckets. See top
// comments for example.
Histogram(const std::string& name,
@@ -200,6 +217,20 @@ class BASE_EXPORT Histogram : public HistogramBase {
Sample maximum,
const BucketRanges* ranges);
+ // Traditionally, histograms allocate their own memory for the bucket
+ // vector but "shared" histograms use memory regions allocated from a
+ // special memory segment that is passed in here. It is assumed that
+ // the life of this memory is managed externally and exceeds the lifetime
+ // of this object. Practically, this memory is never released until the
+ // process exits and the OS cleans it up.
+ Histogram(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta);
+
~Histogram() override;
// HistogramBase implementation:
@@ -310,6 +341,15 @@ class BASE_EXPORT LinearHistogram : public Histogram {
size_t bucket_count,
int32_t flags);
+ // Get a histogram using data in persistent storage.
+ static HistogramBase* PersistentGet(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta);
+
struct DescriptionPair {
Sample sample;
const char* description; // Null means end of a list of pairs.
@@ -336,11 +376,21 @@ class BASE_EXPORT LinearHistogram : public Histogram {
HistogramType GetHistogramType() const override;
protected:
+ class Factory;
+
LinearHistogram(const std::string& name,
Sample minimum,
Sample maximum,
const BucketRanges* ranges);
+ LinearHistogram(const std::string& name,
+ Sample minimum,
+ Sample maximum,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta);
+
double GetBucketSize(Count current, size_t i) const override;
// If we have a description for a bucket, then return that. Otherwise
@@ -377,10 +427,23 @@ class BASE_EXPORT BooleanHistogram : public LinearHistogram {
// call sites.
static HistogramBase* FactoryGet(const char* name, int32_t flags);
+ // Get a histogram using data in persistent storage.
+ static HistogramBase* PersistentGet(const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ HistogramSamples::Metadata* meta);
+
HistogramType GetHistogramType() const override;
+ protected:
+ class Factory;
+
private:
BooleanHistogram(const std::string& name, const BucketRanges* ranges);
+ BooleanHistogram(const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ HistogramSamples::Metadata* meta);
friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo(
base::PickleIterator* iter);
@@ -409,6 +472,13 @@ class BASE_EXPORT CustomHistogram : public Histogram {
const std::vector<Sample>& custom_ranges,
int32_t flags);
+ // Get a histogram using data in persistent storage.
+ static HistogramBase* PersistentGet(const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta);
+
// Overridden from Histogram:
HistogramType GetHistogramType() const override;
@@ -421,9 +491,17 @@ class BASE_EXPORT CustomHistogram : public Histogram {
static std::vector<Sample> ArrayToCustomRanges(const Sample* values,
size_t num_values);
protected:
+ class Factory;
+
CustomHistogram(const std::string& name,
const BucketRanges* ranges);
+ CustomHistogram(const std::string& name,
+ const BucketRanges* ranges,
+ HistogramBase::AtomicCount* counts,
+ size_t counts_size,
+ HistogramSamples::Metadata* meta);
+
// HistogramBase implementation:
bool SerializeInfoImpl(base::Pickle* pickle) const override;
@@ -435,8 +513,6 @@ class BASE_EXPORT CustomHistogram : public Histogram {
static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter);
static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges);
- static BucketRanges* CreateBucketRangesFromCustomRanges(
- const std::vector<Sample>& custom_ranges);
DISALLOW_COPY_AND_ASSIGN(CustomHistogram);
};
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h
index 4fa07c6..d8bd0f4 100644
--- a/base/metrics/histogram_base.h
+++ b/base/metrics/histogram_base.h
@@ -21,6 +21,7 @@
namespace base {
+class BucketRanges;
class DictionaryValue;
class HistogramBase;
class HistogramSamples;
@@ -81,6 +82,13 @@ class BASE_EXPORT HistogramBase {
// to shortcut looking up the callback if it doesn't exist.
kCallbackExists = 0x20,
+ // Indicates that the histogram is held in "persistent" memory and may
+ // be accessible between processes. This is only possible if such a
+ // memory segment has been created/attached, used to create a Persistent-
+ // MemoryAllocator, and that loaded into the Histogram module before this
+ // histogram is created.
+ kIsPersistent = 0x40,
+
// Only for Histogram and its sub classes: fancy bucket-naming support.
kHexRangePrintingFlag = 0x8000,
};
diff --git a/base/metrics/histogram_delta_serialization.cc b/base/metrics/histogram_delta_serialization.cc
index f2b825b..3e5d154 100644
--- a/base/metrics/histogram_delta_serialization.cc
+++ b/base/metrics/histogram_delta_serialization.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_snapshot_manager.h"
+#include "base/metrics/statistics_recorder.h"
#include "base/numerics/safe_conversions.h"
#include "base/pickle.h"
#include "base/values.h"
@@ -60,7 +61,8 @@ HistogramDeltaSerialization::~HistogramDeltaSerialization() {
}
void HistogramDeltaSerialization::PrepareAndSerializeDeltas(
- std::vector<std::string>* serialized_deltas) {
+ std::vector<std::string>* serialized_deltas,
+ bool include_persistent) {
DCHECK(thread_checker_.CalledOnValidThread());
serialized_deltas_ = serialized_deltas;
@@ -68,6 +70,7 @@ void HistogramDeltaSerialization::PrepareAndSerializeDeltas(
// the histograms, so that the receiving process can distinguish them from the
// local histograms.
histogram_snapshot_manager_.PrepareDeltas(
+ StatisticsRecorder::begin(include_persistent), StatisticsRecorder::end(),
Histogram::kIPCSerializationSourceFlag, Histogram::kNoFlags);
serialized_deltas_ = NULL;
}
diff --git a/base/metrics/histogram_delta_serialization.h b/base/metrics/histogram_delta_serialization.h
index 0a3983f..a05a1a7 100644
--- a/base/metrics/histogram_delta_serialization.h
+++ b/base/metrics/histogram_delta_serialization.h
@@ -28,9 +28,12 @@ class BASE_EXPORT HistogramDeltaSerialization : public HistogramFlattener {
// Computes deltas in histogram bucket counts relative to the previous call to
// this method. Stores the deltas in serialized form into |serialized_deltas|.
- // If |serialized_deltas| is NULL, no data is serialized, though the next call
- // will compute the deltas relative to this one.
- void PrepareAndSerializeDeltas(std::vector<std::string>* serialized_deltas);
+ // If |serialized_deltas| is null, no data is serialized, though the next call
+ // will compute the deltas relative to this one. Setting |include_persistent|
+ // will include histograms held in persistent memory (and thus may be reported
+ // elsewhere); otherwise only histograms local to this process are serialized.
+ void PrepareAndSerializeDeltas(std::vector<std::string>* serialized_deltas,
+ bool include_persistent);
// Deserialize deltas and add samples to corresponding histograms, creating
// them if necessary. Silently ignores errors in |serialized_deltas|.
diff --git a/base/metrics/histogram_delta_serialization_unittest.cc b/base/metrics/histogram_delta_serialization_unittest.cc
index b53520c..93f7198 100644
--- a/base/metrics/histogram_delta_serialization_unittest.cc
+++ b/base/metrics/histogram_delta_serialization_unittest.cc
@@ -18,7 +18,7 @@ TEST(HistogramDeltaSerializationTest, DeserializeHistogramAndAddSamples) {
HistogramDeltaSerialization serializer("HistogramDeltaSerializationTest");
std::vector<std::string> deltas;
// Nothing was changed yet.
- serializer.PrepareAndSerializeDeltas(&deltas);
+ serializer.PrepareAndSerializeDeltas(&deltas, true);
EXPECT_TRUE(deltas.empty());
HistogramBase* histogram = Histogram::FactoryGet(
@@ -28,7 +28,7 @@ TEST(HistogramDeltaSerializationTest, DeserializeHistogramAndAddSamples) {
histogram->Add(100);
histogram->Add(1000);
- serializer.PrepareAndSerializeDeltas(&deltas);
+ serializer.PrepareAndSerializeDeltas(&deltas, true);
EXPECT_FALSE(deltas.empty());
HistogramDeltaSerialization::DeserializeAndAddSamples(deltas);
diff --git a/base/metrics/histogram_persistence.cc b/base/metrics/histogram_persistence.cc
new file mode 100644
index 0000000..6b4cad4
--- /dev/null
+++ b/base/metrics/histogram_persistence.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2015 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/histogram_persistence.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+namespace {
+
+// Enumerate possible creation results for reporting.
+enum CreateHistogramResultType {
+ // Everything was fine.
+ CREATE_HISTOGRAM_SUCCESS = 0,
+
+ // Pointer to metadata was not valid.
+ CREATE_HISTOGRAM_INVALID_METADATA_POINTER,
+
+ // Histogram metadata was not valid.
+ CREATE_HISTOGRAM_INVALID_METADATA,
+
+ // Ranges information was not valid.
+ CREATE_HISTOGRAM_INVALID_RANGES_ARRAY,
+
+ // Counts information was not valid.
+ CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY,
+
+ // Could not allocate histogram memory due to corruption.
+ CREATE_HISTOGRAM_ALLOCATOR_CORRUPT,
+
+ // Could not allocate histogram memory due to lack of space.
+ CREATE_HISTOGRAM_ALLOCATOR_FULL,
+
+ // Could not allocate histogram memory due to unknown error.
+ CREATE_HISTOGRAM_ALLOCATOR_ERROR,
+
+ // Always keep this at the end.
+ CREATE_HISTOGRAM_MAX
+};
+
+// Type identifiers used when storing in persistent memory so they can be
+// identified during extraction; the first 4 bytes of the SHA1 of the name
+// is used as a unique integer. A "version number" is added to the base
+// so that, if the structure of that object changes, stored older versions
+// will be safely ignored.
+enum : uint32_t {
+ kTypeIdHistogram = 0xF1645910 + 1, // SHA1(Histogram) v1
+ kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1
+ kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1
+};
+
+// This data must be held in persistent memory in order for processes to
+// locate and use histograms created elsewhere.
+struct PersistentHistogramData {
+ int histogram_type;
+ int flags;
+ int minimum;
+ int maximum;
+ size_t bucket_count;
+ PersistentMemoryAllocator::Reference ranges_ref;
+ uint32_t ranges_checksum;
+ PersistentMemoryAllocator::Reference counts_ref;
+ HistogramSamples::Metadata samples_metadata;
+
+ // Space for the histogram name will be added during the actual allocation
+ // request. This must be the last field of the structure. A zero-size array
+ // or a "flexible" array would be preferred but is not (yet) valid C++.
+ char name[1];
+};
+
+// The object held here will obviously not be destructed at process exit
+// but that's okay since PersistentMemoryAllocator objects are explicitly
+// forbidden from doing anything essential at exit anyway due to the fact
+// that they depend on data managed elsewhere and which could be destructed
+// first.
+PersistentMemoryAllocator* g_allocator = nullptr;
+
+// Take an array of range boundaries and create a proper BucketRanges object
+// which is returned to the caller. A return of nullptr indicates that the
+// passed boundaries are invalid.
+BucketRanges* CreateRangesFromData(HistogramBase::Sample* ranges_data,
+ uint32_t ranges_checksum,
+ size_t count) {
+ scoped_ptr<BucketRanges> ranges(new BucketRanges(count));
+ DCHECK_EQ(count, ranges->size());
+ for (size_t i = 0; i < count; ++i) {
+ if (i > 0 && ranges_data[i] <= ranges_data[i - 1])
+ return nullptr;
+ ranges->set_range(i, ranges_data[i]);
+ }
+
+ ranges->ResetChecksum();
+ if (ranges->checksum() != ranges_checksum)
+ return nullptr;
+
+ return ranges.release();
+}
+
+} // namespace
+
+const Feature kPersistentHistogramsFeature{
+ "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT
+};
+
+// Get the histogram in which create results are stored. This is copied almost
+// exactly from the STATIC_HISTOGRAM_POINTER_BLOCK macro but with added code
+// to prevent recursion (a likely occurance because the creation of a new
+// histogram can end up calling this.)
+HistogramBase* GetCreateHistogramResultHistogram() {
+ static base::subtle::AtomicWord atomic_histogram_pointer = 0;
+ HistogramBase* histogram_pointer(
+ reinterpret_cast<HistogramBase*>(
+ base::subtle::Acquire_Load(&atomic_histogram_pointer)));
+ if (!histogram_pointer) {
+ // It's possible for multiple threads to make it here in parallel but
+ // they'll always return the same result as there is a mutex in the Get.
+ // The purpose of the "initialized" variable is just to ensure that
+ // the same thread doesn't recurse which is also why it doesn't have
+ // to be atomic.
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ histogram_pointer = LinearHistogram::FactoryGet(
+ "UMA.CreatePersistentHistogram.Result",
+ 1, CREATE_HISTOGRAM_MAX, CREATE_HISTOGRAM_MAX + 1,
+ HistogramBase::kUmaTargetedHistogramFlag);
+ base::subtle::Release_Store(
+ &atomic_histogram_pointer,
+ reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer));
+ }
+ }
+ return histogram_pointer;
+}
+
+// Record the result of a histogram creation.
+void RecordCreateHistogramResult(CreateHistogramResultType result) {
+ HistogramBase* result_histogram = GetCreateHistogramResultHistogram();
+ if (result_histogram)
+ result_histogram->Add(result);
+}
+
+void SetPersistentHistogramMemoryAllocator(
+ PersistentMemoryAllocator* allocator) {
+ // Releasing or changing an allocator is extremely dangerous because it
+ // likely has histograms stored within it. If the backing memory is also
+ // also released, future accesses to those histograms will seg-fault.
+ // It's not a fatal CHECK() because tests do this knowing that all
+ // such persistent histograms have already been forgotten.
+ if (g_allocator) {
+ LOG(WARNING) << "Active PersistentMemoryAllocator has been released."
+ << " Some existing histogram pointers may be invalid.";
+ delete g_allocator;
+ }
+ g_allocator = allocator;
+}
+
+PersistentMemoryAllocator* GetPersistentHistogramMemoryAllocator() {
+ return g_allocator;
+}
+
+PersistentMemoryAllocator* ReleasePersistentHistogramMemoryAllocator() {
+ PersistentMemoryAllocator* allocator = g_allocator;
+ g_allocator = nullptr;
+ return allocator;
+};
+
+HistogramBase* CreatePersistentHistogram(
+ PersistentMemoryAllocator* allocator,
+ PersistentHistogramData* histogram_data_ptr) {
+ if (!histogram_data_ptr) {
+ RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA_POINTER);
+ NOTREACHED();
+ return nullptr;
+ }
+
+ // Copy the histogram_data to local storage because anything in persistent
+ // memory cannot be trusted as it could be changed at any moment by a
+ // malicious actor that shares access. The contents of histogram_data are
+ // validated below; the local copy is to ensure that the contents cannot
+ // be externally changed between validation and use.
+ PersistentHistogramData histogram_data = *histogram_data_ptr;
+
+ HistogramBase::Sample* ranges_data =
+ allocator->GetAsObject<HistogramBase::Sample>(histogram_data.ranges_ref,
+ kTypeIdRangesArray);
+ if (!ranges_data || histogram_data.bucket_count < 2 ||
+ histogram_data.bucket_count + 1 >
+ std::numeric_limits<size_t>::max() / sizeof(HistogramBase::Sample) ||
+ allocator->GetAllocSize(histogram_data.ranges_ref) <
+ (histogram_data.bucket_count + 1) * sizeof(HistogramBase::Sample)) {
+ RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY);
+ NOTREACHED();
+ return nullptr;
+ }
+ // To avoid racy destruction at shutdown, the following will be leaked.
+ const BucketRanges* ranges = CreateRangesFromData(
+ ranges_data,
+ histogram_data.ranges_checksum,
+ histogram_data.bucket_count + 1);
+ if (!ranges) {
+ RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_RANGES_ARRAY);
+ NOTREACHED();
+ return nullptr;
+ }
+ ranges = StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges);
+
+ HistogramBase::AtomicCount* counts_data =
+ allocator->GetAsObject<HistogramBase::AtomicCount>(
+ histogram_data.counts_ref, kTypeIdCountsArray);
+ if (!counts_data ||
+ allocator->GetAllocSize(histogram_data.counts_ref) <
+ histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) {
+ RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY);
+ NOTREACHED();
+ return nullptr;
+ }
+
+ std::string name(histogram_data_ptr->name);
+ HistogramBase* histogram = nullptr;
+ switch (histogram_data.histogram_type) {
+ case HISTOGRAM:
+ histogram = Histogram::PersistentGet(
+ name,
+ histogram_data.minimum,
+ histogram_data.maximum,
+ ranges,
+ counts_data,
+ histogram_data.bucket_count,
+ &histogram_data_ptr->samples_metadata);
+ break;
+ case LINEAR_HISTOGRAM:
+ histogram = LinearHistogram::PersistentGet(
+ name,
+ histogram_data.minimum,
+ histogram_data.maximum,
+ ranges,
+ counts_data,
+ histogram_data.bucket_count,
+ &histogram_data_ptr->samples_metadata);
+ break;
+ case BOOLEAN_HISTOGRAM:
+ histogram = BooleanHistogram::PersistentGet(
+ name,
+ ranges,
+ counts_data,
+ &histogram_data_ptr->samples_metadata);
+ break;
+ case CUSTOM_HISTOGRAM:
+ histogram = CustomHistogram::PersistentGet(
+ name,
+ ranges,
+ counts_data,
+ histogram_data.bucket_count,
+ &histogram_data_ptr->samples_metadata);
+ break;
+ }
+
+ if (histogram) {
+ DCHECK_EQ(histogram_data.histogram_type, histogram->GetHistogramType());
+ histogram->SetFlags(histogram_data.flags);
+ }
+
+ RecordCreateHistogramResult(CREATE_HISTOGRAM_SUCCESS);
+ return histogram;
+}
+
+HistogramBase* GetPersistentHistogram(
+ PersistentMemoryAllocator* allocator,
+ int32_t ref) {
+ // Unfortunately, the above "pickle" methods cannot be used as part of the
+ // persistance because the deserialization methods always create local
+ // count data (these must referenced the persistent counts) and always add
+ // it to the local list of known histograms (these may be simple references
+ // to histograms in other processes).
+ PersistentHistogramData* histogram_data =
+ allocator->GetAsObject<PersistentHistogramData>(ref, kTypeIdHistogram);
+ size_t length = allocator->GetAllocSize(ref);
+ if (!histogram_data ||
+ reinterpret_cast<char*>(histogram_data)[length - 1] != '\0') {
+ RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_METADATA);
+ NOTREACHED();
+ return nullptr;
+ }
+ return CreatePersistentHistogram(allocator, histogram_data);
+}
+
+HistogramBase* GetNextPersistentHistogram(
+ PersistentMemoryAllocator* allocator,
+ PersistentMemoryAllocator::Iterator* iter) {
+ PersistentMemoryAllocator::Reference ref;
+ uint32_t type_id;
+ while ((ref = allocator->GetNextIterable(iter, &type_id)) != 0) {
+ if (type_id == kTypeIdHistogram)
+ return GetPersistentHistogram(allocator, ref);
+ }
+ return nullptr;
+}
+
+void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref,
+ bool registered) {
+ // If the created persistent histogram was registered then it needs to
+ // be marked as "iterable" in order to be found by other processes.
+ if (registered)
+ GetPersistentHistogramMemoryAllocator()->MakeIterable(ref);
+ // If it wasn't registered then a race condition must have caused
+ // two to be created. The allocator does not support releasing the
+ // acquired memory so just change the type to be empty.
+ else
+ GetPersistentHistogramMemoryAllocator()->SetType(ref, 0);
+}
+
+HistogramBase* AllocatePersistentHistogram(
+ PersistentMemoryAllocator* allocator,
+ HistogramType histogram_type,
+ const std::string& name,
+ int minimum,
+ int maximum,
+ const BucketRanges* bucket_ranges,
+ int32_t flags,
+ PersistentMemoryAllocator::Reference* ref_ptr) {
+ if (!allocator)
+ return nullptr;
+
+ size_t bucket_count = bucket_ranges->bucket_count();
+ // An overflow such as this, perhaps as the result of a milicious actor,
+ // could lead to writing beyond the allocation boundary and into other
+ // memory. Just fail the allocation and let the caller deal with it.
+ if (bucket_count > std::numeric_limits<int32_t>::max() /
+ sizeof(HistogramBase::AtomicCount)) {
+ NOTREACHED();
+ return nullptr;
+ }
+ size_t counts_bytes = bucket_count * sizeof(HistogramBase::AtomicCount);
+ size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample);
+ PersistentMemoryAllocator::Reference ranges_ref =
+ allocator->Allocate(ranges_bytes, kTypeIdRangesArray);
+ PersistentMemoryAllocator::Reference counts_ref =
+ allocator->Allocate(counts_bytes, kTypeIdCountsArray);
+ PersistentMemoryAllocator::Reference histogram_ref =
+ allocator->Allocate(offsetof(PersistentHistogramData, name) +
+ name.length() + 1, kTypeIdHistogram);
+ HistogramBase::Sample* ranges_data =
+ allocator->GetAsObject<HistogramBase::Sample>(ranges_ref,
+ kTypeIdRangesArray);
+ PersistentHistogramData* histogram_data =
+ allocator->GetAsObject<PersistentHistogramData>(histogram_ref,
+ kTypeIdHistogram);
+
+ // Only continue here if all allocations were successful. If they weren't
+ // there is no way to free the space but that's not really a problem since
+ // the allocations only fail because the space is full and so any future
+ // attempts will also fail.
+ if (counts_ref && ranges_data && histogram_data) {
+ strcpy(histogram_data->name, name.c_str());
+ for (size_t i = 0; i < bucket_ranges->size(); ++i)
+ ranges_data[i] = bucket_ranges->range(i);
+
+ histogram_data->histogram_type = histogram_type;
+ histogram_data->flags = flags;
+ histogram_data->minimum = minimum;
+ histogram_data->maximum = maximum;
+ histogram_data->bucket_count = bucket_count;
+ histogram_data->ranges_ref = ranges_ref;
+ histogram_data->ranges_checksum = bucket_ranges->checksum();
+ histogram_data->counts_ref = counts_ref;
+
+ // Create the histogram using resources in persistent memory. This ends up
+ // resolving the "ref" values stored in histogram_data instad of just
+ // using what is already known above but avoids duplicating the switch
+ // statement here and serves as a double-check that everything is
+ // correct before commiting the new histogram to persistent space.
+ HistogramBase* histogram =
+ CreatePersistentHistogram(allocator, histogram_data);
+ DCHECK(histogram);
+ if (ref_ptr != nullptr)
+ *ref_ptr = histogram_ref;
+ return histogram;
+ }
+
+ CreateHistogramResultType result;
+ if (allocator->IsCorrupt()) {
+ result = CREATE_HISTOGRAM_ALLOCATOR_CORRUPT;
+ } else if (allocator->IsFull()) {
+ result = CREATE_HISTOGRAM_ALLOCATOR_FULL;
+ } else {
+ result = CREATE_HISTOGRAM_ALLOCATOR_ERROR;
+ }
+ RecordCreateHistogramResult(result);
+
+ return nullptr;
+}
+
+void ImportPersistentHistograms() {
+ // Each call resumes from where it last left off so need persistant iterator.
+ // The lock protects against concurrent access to the iterator and is created
+ // dynamically so as to not require destruction during program exit.
+ static PersistentMemoryAllocator::Iterator iter;
+ static base::Lock* lock = new base::Lock();
+
+ if (g_allocator) {
+ base::AutoLock auto_lock(*lock);
+ if (iter.is_clear())
+ g_allocator->CreateIterator(&iter);
+
+ for (;;) {
+ HistogramBase* histogram = GetNextPersistentHistogram(g_allocator, &iter);
+ if (!histogram)
+ break;
+ StatisticsRecorder::RegisterOrDeleteDuplicate(histogram);
+ }
+ }
+}
+
+} // namespace base
diff --git a/base/metrics/histogram_persistence.h b/base/metrics/histogram_persistence.h
new file mode 100644
index 0000000..1683713
--- /dev/null
+++ b/base/metrics/histogram_persistence.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2015 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_HISTOGRAM_PERSISTENCE_H_
+#define BASE_METRICS_HISTOGRAM_PERSISTENCE_H_
+
+#include "base/base_export.h"
+#include "base/feature_list.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/persistent_memory_allocator.h"
+
+namespace base {
+
+// Feature definition for enabling histogram persistence.
+BASE_EXPORT extern const Feature kPersistentHistogramsFeature;
+
+// Histogram containing creation results. Visible for testing.
+BASE_EXPORT HistogramBase* GetCreateHistogramResultHistogram();
+
+// Access a PersistentMemoryAllocator for storing histograms in space that
+// can be persisted or shared between processes. There is only ever one
+// allocator for all such histograms created by a single process though one
+// process may access the histograms created by other processes if it has a
+// handle on its memory segment. This takes ownership of the object and
+// should not be changed without great care as it is likely that there will
+// be pointers to data held in that space.
+BASE_EXPORT void SetPersistentHistogramMemoryAllocator(
+ PersistentMemoryAllocator* allocator);
+BASE_EXPORT PersistentMemoryAllocator* GetPersistentHistogramMemoryAllocator();
+
+// This access to the persistent allocator is only for testing; it extracts
+// the current allocator completely. This allows easy creation of histograms
+// within persistent memory segments which can then be extracted and used
+// in other ways.
+BASE_EXPORT PersistentMemoryAllocator*
+ReleasePersistentHistogramMemoryAllocator();
+
+// Recreate a Histogram from data held in persistent memory. Though this
+// object will be local to the current process, the sample data will be
+// shared with all other threads referencing it. This method takes a |ref|
+// to the top- level histogram data and the |allocator| on which it is found.
+// This method will return nullptr if any problem is detected with the data.
+// The |allocator| may or may not be the same as the PersistentMemoryAllocator
+// set for general use so that this method can be used to extract Histograms
+// from persistent memory segments other than the default place that this
+// process is creating its own histograms. The caller must take ownership of
+// the returned object and destroy it when no longer needed.
+BASE_EXPORT HistogramBase* GetPersistentHistogram(
+ PersistentMemoryAllocator* allocator,
+ int32_t ref);
+
+// Get the next histogram in persistent data based on iterator. The caller
+// must take ownership of the returned object and destroy it when no longer
+// needed.
+BASE_EXPORT HistogramBase* GetNextPersistentHistogram(
+ PersistentMemoryAllocator* allocator,
+ PersistentMemoryAllocator::Iterator* iter);
+
+// Finalize the creation of the histogram, making it available to other
+// processes if it is the registered instance.
+void FinalizePersistentHistogram(PersistentMemoryAllocator::Reference ref,
+ bool register);
+
+// Allocate a new persistent histogram. This does *not* make the object
+// iterable in the allocator; call MakeIterable(ref) directly if that is
+// desired.
+BASE_EXPORT HistogramBase* AllocatePersistentHistogram(
+ PersistentMemoryAllocator* allocator,
+ HistogramType histogram_type,
+ const std::string& name,
+ int minimum,
+ int maximum,
+ const BucketRanges* bucket_ranges,
+ int32_t flags,
+ PersistentMemoryAllocator::Reference* ref_ptr);
+
+// Import new histograms from attached PersistentMemoryAllocator. It's
+// possible for other processes to create histograms in the attached memory
+// segment; this adds those to the internal list of known histograms to
+// avoid creating duplicates that would have to merged during reporting.
+// Every call to this method resumes from the last entry it saw so it costs
+// nothing if nothing new has been added.
+void ImportPersistentHistograms();
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_PERSISTENCE_H_
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc
index 02f87f0..3d1c0d6 100644
--- a/base/metrics/histogram_snapshot_manager.cc
+++ b/base/metrics/histogram_snapshot_manager.cc
@@ -22,20 +22,6 @@ HistogramSnapshotManager::~HistogramSnapshotManager() {
STLDeleteValues(&logged_samples_);
}
-void HistogramSnapshotManager::PrepareDeltas(
- HistogramBase::Flags flag_to_set,
- HistogramBase::Flags required_flags) {
- StatisticsRecorder::Histograms histograms;
- StatisticsRecorder::GetHistograms(&histograms);
- for (StatisticsRecorder::Histograms::const_iterator it = histograms.begin();
- histograms.end() != it;
- ++it) {
- (*it)->SetFlags(flag_to_set);
- if (((*it)->flags() & required_flags) == required_flags)
- PrepareDelta(**it);
- }
-}
-
void HistogramSnapshotManager::PrepareDelta(const HistogramBase& histogram) {
DCHECK(histogram_flattener_);
diff --git a/base/metrics/histogram_snapshot_manager.h b/base/metrics/histogram_snapshot_manager.h
index bad4668..d1ba2a5 100644
--- a/base/metrics/histogram_snapshot_manager.h
+++ b/base/metrics/histogram_snapshot_manager.h
@@ -36,8 +36,17 @@ class BASE_EXPORT HistogramSnapshotManager {
// Only histograms that have all the flags specified by the argument will be
// chosen. If all histograms should be recorded, set it to
// |Histogram::kNoFlags|.
- void PrepareDeltas(HistogramBase::Flags flags_to_set,
- HistogramBase::Flags required_flags);
+ template <class ForwardHistogramIterator>
+ void PrepareDeltas(ForwardHistogramIterator begin,
+ ForwardHistogramIterator end,
+ HistogramBase::Flags flags_to_set,
+ HistogramBase::Flags required_flags) {
+ for (ForwardHistogramIterator it = begin; it != end; ++it) {
+ (*it)->SetFlags(flags_to_set);
+ if (((*it)->flags() & required_flags) == required_flags)
+ PrepareDelta(**it);
+ }
+ }
private:
// Snapshot this histogram, and record the delta.
diff --git a/base/metrics/histogram_snapshot_manager_unittest.cc b/base/metrics/histogram_snapshot_manager_unittest.cc
index b6a367a..72db7d5 100644
--- a/base/metrics/histogram_snapshot_manager_unittest.cc
+++ b/base/metrics/histogram_snapshot_manager_unittest.cc
@@ -64,8 +64,9 @@ TEST_F(HistogramSnapshotManagerTest, PrepareDeltasNoFlagsFilter) {
UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2);
UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);
- histogram_snapshot_manager_.PrepareDeltas(HistogramBase::kNoFlags,
- HistogramBase::kNoFlags);
+ histogram_snapshot_manager_.PrepareDeltas(
+ StatisticsRecorder::begin(false), StatisticsRecorder::end(),
+ HistogramBase::kNoFlags, HistogramBase::kNoFlags);
const std::vector<std::string>& histograms =
histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
@@ -80,6 +81,7 @@ TEST_F(HistogramSnapshotManagerTest, PrepareDeltasUmaHistogramFlagFilter) {
UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);
histogram_snapshot_manager_.PrepareDeltas(
+ StatisticsRecorder::begin(false), StatisticsRecorder::end(),
HistogramBase::kNoFlags, HistogramBase::kUmaTargetedHistogramFlag);
const std::vector<std::string>& histograms =
@@ -95,6 +97,7 @@ TEST_F(HistogramSnapshotManagerTest,
UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);
histogram_snapshot_manager_.PrepareDeltas(
+ StatisticsRecorder::begin(false), StatisticsRecorder::end(),
HistogramBase::kNoFlags, HistogramBase::kUmaStabilityHistogramFlag);
const std::vector<std::string>& histograms =
diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc
index f930288..faaac94 100644
--- a/base/metrics/histogram_unittest.cc
+++ b/base/metrics/histogram_unittest.cc
@@ -16,6 +16,8 @@
#include "base/memory/scoped_ptr.h"
#include "base/metrics/bucket_ranges.h"
#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_persistence.h"
+#include "base/metrics/persistent_memory_allocator.h"
#include "base/metrics/sample_vector.h"
#include "base/metrics/statistics_recorder.h"
#include "base/pickle.h"
@@ -26,13 +28,24 @@ namespace base {
class HistogramTest : public testing::Test {
protected:
+ const int32_t kAllocatorMemorySize = 64 << 10; // 64 KiB
+
+ HistogramTest() {}
+
void SetUp() override {
// Each test will have a clean state (no Histogram / BucketRanges
// registered).
InitializeStatisticsRecorder();
+ // By getting the results-histogram before any persistent allocator
+ // is attached, that histogram is guaranteed not to be stored in
+ // any persistent memory segment (which simplifies some tests).
+ GetCreateHistogramResultHistogram();
}
- void TearDown() override { UninitializeStatisticsRecorder(); }
+ void TearDown() override {
+ UninitializeStatisticsRecorder();
+ DestroyPersistentMemoryAllocator();
+ }
void InitializeStatisticsRecorder() {
statistics_recorder_ = new StatisticsRecorder();
@@ -43,7 +56,30 @@ class HistogramTest : public testing::Test {
statistics_recorder_ = NULL;
}
+ void CreatePersistentMemoryAllocator() {
+ if (!allocator_memory_)
+ allocator_memory_.reset(new char[kAllocatorMemorySize]);
+
+ SetPersistentHistogramMemoryAllocator(nullptr);
+ memset(allocator_memory_.get(), 0, kAllocatorMemorySize);
+ SetPersistentHistogramMemoryAllocator(
+ new PersistentMemoryAllocator(
+ allocator_memory_.get(), kAllocatorMemorySize, 0,
+ 0, "HistogramAllocatorTest", false));
+ allocator_ = GetPersistentHistogramMemoryAllocator();
+ }
+
+ void DestroyPersistentMemoryAllocator() {
+ allocator_ = nullptr;
+ SetPersistentHistogramMemoryAllocator(nullptr);
+ }
+
StatisticsRecorder* statistics_recorder_;
+ scoped_ptr<char[]> allocator_memory_;
+ PersistentMemoryAllocator* allocator_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HistogramTest);
};
// Check for basic syntax and use.
@@ -71,6 +107,92 @@ TEST_F(HistogramTest, BasicTest) {
LOCAL_HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130);
}
+// Check for basic syntax and use.
+TEST_F(HistogramTest, PersistentTest) {
+ CreatePersistentMemoryAllocator();
+ 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;
+ uint32_t type;
+ allocator_->CreateIterator(&iter);
+ EXPECT_NE(0U, allocator_->GetNextIterable(&iter, &type)); // Histogram
+ EXPECT_NE(0U, allocator_->GetNextIterable(&iter, &type)); // LinearHistogram
+ EXPECT_NE(0U, allocator_->GetNextIterable(&iter, &type)); // BooleanHistogram
+ EXPECT_NE(0U, allocator_->GetNextIterable(&iter, &type)); // CustomHistogram
+ EXPECT_EQ(0U, allocator_->GetNextIterable(&iter, &type));
+
+ // Create a second allocator and have it access the memory of the first.
+ scoped_ptr<HistogramBase> recovered;
+ PersistentMemoryAllocator recovery(
+ allocator_memory_.get(), kAllocatorMemorySize, 0,
+ 0, std::string(), false);
+ recovery.CreateIterator(&iter);
+
+ recovered.reset(GetNextPersistentHistogram(&recovery, &iter));
+ ASSERT_TRUE(recovered);
+ recovered->CheckName("TestHistogram");
+
+ recovered.reset(GetNextPersistentHistogram(&recovery, &iter));
+ ASSERT_TRUE(recovered);
+ recovered->CheckName("TestLinearHistogram");
+
+ recovered.reset(GetNextPersistentHistogram(&recovery, &iter));
+ ASSERT_TRUE(recovered);
+ recovered->CheckName("TestBooleanHistogram");
+
+ recovered.reset(GetNextPersistentHistogram(&recovery, &iter));
+ ASSERT_TRUE(recovered);
+ recovered->CheckName("TestCustomHistogram");
+
+ recovered.reset(GetNextPersistentHistogram(&recovery, &iter));
+ EXPECT_FALSE(recovered);
+
+ // Use standard macros (but with fixed samples)
+ LOCAL_HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1));
+ LOCAL_HISTOGRAM_COUNTS("Test3Histogram", 30);
+ LOCAL_HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130);
+}
+
// Check that the macro correctly matches histograms by name and records their
// data together.
TEST_F(HistogramTest, NameMatchTest) {
diff --git a/base/metrics/sample_vector.h b/base/metrics/sample_vector.h
index 0317869f..86319ea 100644
--- a/base/metrics/sample_vector.h
+++ b/base/metrics/sample_vector.h
@@ -54,6 +54,7 @@ class BASE_EXPORT SampleVector : public HistogramSamples {
private:
FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
+ FRIEND_TEST_ALL_PREFIXES(SharedHistogramTest, CorruptSampleCounts);
// In the case where this class manages the memory, here it is.
std::vector<HistogramBase::AtomicCount> local_counts_;
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
index b18efad..1bfa348 100644
--- a/base/metrics/statistics_recorder.cc
+++ b/base/metrics/statistics_recorder.cc
@@ -31,6 +31,36 @@ bool HistogramNameLesser(const base::HistogramBase* a,
namespace base {
+StatisticsRecorder::HistogramIterator::HistogramIterator(
+ const HistogramMap::iterator& iter, bool include_persistent)
+ : iter_(iter),
+ include_persistent_(include_persistent) {
+}
+
+StatisticsRecorder::HistogramIterator::HistogramIterator(
+ const HistogramIterator& rhs)
+ : iter_(rhs.iter_),
+ include_persistent_(rhs.include_persistent_) {
+}
+
+StatisticsRecorder::HistogramIterator::~HistogramIterator() {}
+
+StatisticsRecorder::HistogramIterator&
+StatisticsRecorder::HistogramIterator::operator++() {
+ const HistogramMap::iterator histograms_end = histograms_->end();
+ while (iter_ != histograms_end) {
+ ++iter_;
+ if (iter_ == histograms_end)
+ break;
+ if (!include_persistent_ && (iter_->second->flags() &
+ HistogramBase::kIsPersistent)) {
+ continue;
+ }
+ break;
+ }
+ return *this;
+}
+
// static
void StatisticsRecorder::Initialize() {
// Ensure that an instance of the StatisticsRecorder object is created.
@@ -66,7 +96,8 @@ HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
histogram_to_return = histogram;
} else {
const std::string& name = histogram->histogram_name();
- uint64_t name_hash = histogram->name_hash();
+ const uint64_t name_hash = histogram->name_hash();
+ DCHECK_NE(0U, name_hash);
HistogramMap::iterator it = histograms_->find(name_hash);
if (histograms_->end() == it) {
(*histograms_)[name_hash] = histogram;
@@ -264,7 +295,7 @@ bool StatisticsRecorder::SetCallback(
return false;
callbacks_->insert(std::make_pair(name, cb));
- HistogramMap::iterator it = histograms_->find(HashMetricName(name));
+ auto it = histograms_->find(HashMetricName(name));
if (it != histograms_->end()) {
DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
it->second->SetFlags(HistogramBase::kCallbackExists);
@@ -284,7 +315,7 @@ void StatisticsRecorder::ClearCallback(const std::string& name) {
callbacks_->erase(name);
// We also clear the flag from the histogram (if it exists).
- HistogramMap::iterator it = histograms_->find(HashMetricName(name));
+ auto it = histograms_->find(HashMetricName(name));
if (it != histograms_->end()) {
DCHECK_EQ(name, it->second->histogram_name()) << "hash collision";
it->second->ClearFlags(HistogramBase::kCallbackExists);
@@ -305,7 +336,18 @@ StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
: OnSampleCallback();
}
-// private static
+// static
+StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
+ bool include_persistent) {
+ return HistogramIterator(histograms_->begin(), include_persistent);
+}
+
+// static
+StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
+ return HistogramIterator(histograms_->end(), true);
+}
+
+// static
void StatisticsRecorder::GetSnapshot(const std::string& query,
Histograms* snapshot) {
if (lock_ == NULL)
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h
index b1d182e..ef4481a 100644
--- a/base/metrics/statistics_recorder.h
+++ b/base/metrics/statistics_recorder.h
@@ -31,8 +31,37 @@ class Lock;
class BASE_EXPORT StatisticsRecorder {
public:
+ typedef std::map<uint64_t, HistogramBase*> HistogramMap; // Key is name-hash.
typedef std::vector<HistogramBase*> Histograms;
+ // A class for iterating over the histograms held within this global resource.
+ class BASE_EXPORT HistogramIterator {
+ public:
+ HistogramIterator(const HistogramMap::iterator& iter,
+ bool include_persistent);
+ HistogramIterator(const HistogramIterator& rhs); // Must be copyable.
+ ~HistogramIterator();
+
+ HistogramIterator& operator++();
+ HistogramIterator operator++(int) {
+ HistogramIterator tmp(*this);
+ operator++();
+ return tmp;
+ }
+
+ bool operator==(const HistogramIterator& rhs) const {
+ return iter_ == rhs.iter_;
+ }
+ bool operator!=(const HistogramIterator& rhs) const {
+ return iter_ != rhs.iter_;
+ }
+ HistogramBase* operator*() { return iter_->second; }
+
+ private:
+ HistogramMap::iterator iter_;
+ const bool include_persistent_;
+ };
+
// Initializes the StatisticsRecorder system. Safe to call multiple times.
static void Initialize();
@@ -72,6 +101,10 @@ class BASE_EXPORT StatisticsRecorder {
// safe. It returns NULL if a matching histogram is not found.
static HistogramBase* FindHistogram(const std::string& name);
+ // Support for iterating over known histograms.
+ static HistogramIterator begin(bool include_persistent);
+ static HistogramIterator end();
+
// GetSnapshot copies some of the pointers to registered histograms into the
// caller supplied vector (Histograms). Only histograms which have |query| as
// a substring are copied (an empty string will process all registered
@@ -97,10 +130,6 @@ class BASE_EXPORT StatisticsRecorder {
static OnSampleCallback FindCallback(const std::string& histogram_name);
private:
- // We keep all registered histograms in a map, indexed by the hash of the
- // name of the histogram.
- typedef std::map<uint64_t, HistogramBase*> HistogramMap;
-
// We keep a map of callbacks to histograms, so that as histograms are
// created, we can set the callback properly.
typedef std::map<std::string, OnSampleCallback> CallbackMap;
@@ -115,6 +144,7 @@ class BASE_EXPORT StatisticsRecorder {
friend class HistogramSnapshotManagerTest;
friend class HistogramTest;
friend class JsonPrefStoreTest;
+ friend class SharedHistogramTest;
friend class SparseHistogramTest;
friend class StatisticsRecorderTest;
FRIEND_TEST_ALL_PREFIXES(HistogramDeltaSerializationTest,
diff --git a/base/metrics/statistics_recorder_unittest.cc b/base/metrics/statistics_recorder_unittest.cc
index af5c1e7..3e19ec3 100644
--- a/base/metrics/statistics_recorder_unittest.cc
+++ b/base/metrics/statistics_recorder_unittest.cc
@@ -10,6 +10,7 @@
#include "base/json/json_reader.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_persistence.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/statistics_recorder.h"
#include "base/values.h"
@@ -20,12 +21,18 @@ namespace base {
class StatisticsRecorderTest : public testing::Test {
protected:
void SetUp() override {
+ // Get this first so it never gets created in persistent storage and will
+ // not appear in the StatisticsRecorder after it is re-initialized.
+ GetCreateHistogramResultHistogram();
// Each test will have a clean state (no Histogram / BucketRanges
// registered).
InitializeStatisticsRecorder();
}
- void TearDown() override { UninitializeStatisticsRecorder(); }
+ void TearDown() override {
+ UninitializeStatisticsRecorder();
+ SetPersistentHistogramMemoryAllocator(nullptr);
+ }
void InitializeStatisticsRecorder() {
statistics_recorder_ = new StatisticsRecorder();
@@ -315,6 +322,23 @@ TEST_F(StatisticsRecorderTest, ToJSON) {
EXPECT_TRUE(json.empty());
}
+TEST_F(StatisticsRecorderTest, IterationTest) {
+ StatisticsRecorder::Histograms registered_histograms;
+ LOCAL_HISTOGRAM_COUNTS("TestHistogram.IterationTest1", 30);
+ SetPersistentHistogramMemoryAllocator(
+ new LocalPersistentMemoryAllocator(64 << 10, 0, std::string()));
+ LOCAL_HISTOGRAM_COUNTS("TestHistogram.IterationTest2", 30);
+
+ StatisticsRecorder::HistogramIterator i1 = StatisticsRecorder::begin(true);
+ EXPECT_NE(StatisticsRecorder::end(), i1);
+ EXPECT_NE(StatisticsRecorder::end(), ++i1);
+ EXPECT_EQ(StatisticsRecorder::end(), ++i1);
+
+ StatisticsRecorder::HistogramIterator i2 = StatisticsRecorder::begin(false);
+ EXPECT_NE(StatisticsRecorder::end(), i2);
+ EXPECT_EQ(StatisticsRecorder::end(), ++i2);
+}
+
namespace {
// CallbackCheckWrapper is simply a convenient way to check and store that