Index: base/metrics/histogram.cc |
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc |
index 62c2bc808dad38aac0c125ee9ab6bd8d61de52c3..a1a6ac7f86277f5cae0f76c7b66e9daab3b8b0a6 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 |