Chromium Code Reviews| Index: base/metrics/histogram.cc |
| diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc |
| index 62c2bc808dad38aac0c125ee9ab6bd8d61de52c3..b8a7d8ab76d0017e1d5dd0ba759849d613626a0e 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,147 @@ 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); |
|
Alexei Svitkine (slow)
2016/01/12 21:54:44
Nit: Remove the extra line.
bcwhite
2016/01/13 13:38:29
Done.
|
| - HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); |
| +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) {} |
| + |
| + 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; |
| + } |
| + |
| + // 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() { |
| + 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); |
| + |
| + // Sometimes the bucket info is dynamically determined in 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); |
| + 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); |
| + } |
| - tentative_histogram->SetFlags(flags); |
| + // 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); |
| + } |
| + |
| + 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 +254,17 @@ HistogramBase* Histogram::FactoryTimeGet(const char* name, |
| flags); |
| } |
| +HistogramBase* Histogram::PersistentGet(const char* 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 +459,22 @@ 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) |
|
Alexei Svitkine (slow)
2016/01/12 21:54:44
Nit: {}'s since body is multi-line.
bcwhite
2016/01/13 13:38:29
Done.
|
| + samples_.reset(new SampleVector(HashMetricName(name), |
| + counts, counts_size, meta, ranges)); |
| +} |
| + |
| Histogram::~Histogram() { |
| } |
| @@ -545,6 +676,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 +756,18 @@ HistogramBase* LinearHistogram::FactoryTimeGet(const char* name, |
| flags); |
| } |
| +HistogramBase* LinearHistogram::PersistentGet( |
| + const char* 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 +779,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 +794,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 +872,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() { |
| + 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 char* 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 +917,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 +949,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() { |
| + // 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 +1001,15 @@ HistogramBase* CustomHistogram::FactoryGet( |
| return FactoryGet(std::string(name), custom_ranges, flags); |
| } |
| +HistogramBase* CustomHistogram::PersistentGet( |
| + const char* 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 +1036,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 +1111,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 |