Index: base/metrics/histogram.cc |
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc |
index 083856bf87618379a964eb2095aa6cae4d7039c1..d2a502b7475111e7cd8828ad2ba35528eafea748 100644 |
--- a/base/metrics/histogram.cc |
+++ b/base/metrics/histogram.cc |
@@ -18,7 +18,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" |
@@ -83,45 +85,138 @@ 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 flags) { |
- bool valid_arguments = |
- InspectConstructionArguments(name, &minimum, &maximum, &bucket_count); |
- DCHECK(valid_arguments); |
- 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 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 flags) |
+ : name_(name), |
+ histogram_type_(histogram_type), |
+ minimum_(minimum), |
+ maximum_(maximum), |
+ bucket_count_(bucket_count), |
+ flags_(flags) {} |
+ |
+ virtual BucketRanges* CreateRanges() { |
+ BucketRanges* ranges = new BucketRanges(bucket_count_ + 1); |
+ Histogram::InitializeBucketRanges(minimum_, maximum_, ranges); |
+ return ranges; |
+ } |
+ |
+ virtual HistogramBase* HeapAlloc(const BucketRanges* ranges) { |
+ return new Histogram(name_, minimum_, maximum_, ranges); |
+ } |
+ |
+ virtual void FillHistogram(HistogramBase* histogram) {} |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Please document these methods and the class.
bcwhite
2015/12/22 20:50:38
Done.
|
+ |
+ const std::string& name_; |
+ const HistogramType histogram_type_; |
+ HistogramBase::Sample minimum_; |
+ HistogramBase::Sample maximum_; |
+ size_t bucket_count_; |
+ int32 flags_; |
+}; |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Add a private: section and DISALLOW_COPY_AND_ASSIG
bcwhite
2015/12/22 20:50:37
Done.
|
+ |
+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 create_ranges(). |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Nit: create_ranges() -> CreateRanges().
bcwhite
2015/12/22 20:50:37
Done.
|
+ if (bucket_count_ == 0) { |
+ bucket_count_ = registered_ranges->bucket_count(); |
+ minimum_ = registered_ranges->range(1); |
+ maximum_ = registered_ranges->range(bucket_count_ - 1); |
+ } |
+ |
+ 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); |
+ } |
+ if (!tentative_histogram) { |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Either make this an else or mention in what legit
bcwhite
2015/12/22 20:50:38
Done.
|
+ flags_ &= ~HistogramBase::kIsPersistent; |
+ tentative_histogram = HeapAlloc(registered_ranges); |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Should this also clear histogram_ref?
Or is the e
bcwhite
2015/12/22 20:50:37
Done.
|
+ } |
- Histogram* tentative_histogram = |
- new Histogram(name, minimum, maximum, registered_ranges); |
+ FillHistogram(tentative_histogram); |
- tentative_histogram->SetFlags(flags); |
+ tentative_histogram->SetFlags(flags_); |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Nit: Move this about FillHistogram() and don't sep
bcwhite
2015/12/22 20:50:37
That will require that any overriden FillHistogram
|
histogram = |
StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); |
+ |
+ // Persistent histograms need some follow-up processing. |
+ if (histogram_ref) { |
+ // If the created persistent histogram is canonical then it needs to be |
+ // marked as "iterable" in order to be found by other processes. |
+ if (histogram == tentative_histogram) |
+ GetPersistentHistogramMemoryAllocator()->MakeIterable(histogram_ref); |
+ // If it's not the canonical one 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(histogram_ref, 0); |
Alexei Svitkine (slow)
2015/12/21 19:32:11
Can this logic be contained in the allocator? i.e.
bcwhite
2015/12/22 20:50:38
Done.
|
+ } |
} |
- 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 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, |
@@ -343,6 +438,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) |
+ samples_.reset(new SampleVector(HashMetricName(name), |
+ counts, counts_size, meta, ranges)); |
+} |
+ |
Histogram::~Histogram() { |
} |
@@ -544,7 +655,43 @@ void Histogram::GetCountAndBucketData(Count* count, |
// buckets. |
//------------------------------------------------------------------------------ |
-LinearHistogram::~LinearHistogram() {} |
+class LinearHistogram::Factory : public Histogram::Factory { |
+ public: |
+ Factory(const std::string& name, |
+ HistogramBase::Sample minimum, |
+ HistogramBase::Sample maximum, |
+ size_t bucket_count, |
+ int32 flags, |
+ const DescriptionPair* descriptions) |
+ : Histogram::Factory(name, LINEAR_HISTOGRAM, minimum, maximum, |
+ bucket_count, flags) { |
+ descriptions_ = descriptions; |
+ } |
+ |
+ protected: |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Nit: private:
and add DISALLOW_COPY_AND_ASSIGN()
bcwhite
2015/12/22 20:50:37
Done.
|
+ 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 { |
+ 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; |
+ } |
+ } |
+ } |
+ |
+ const DescriptionPair* descriptions_; |
+}; |
HistogramBase* LinearHistogram::FactoryGet(const std::string& name, |
Sample minimum, |
@@ -593,42 +740,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 { |
@@ -642,6 +755,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) |
@@ -705,26 +833,26 @@ HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) { |
// This section provides implementation for BooleanHistogram. |
//------------------------------------------------------------------------------ |
-HistogramBase* BooleanHistogram::FactoryGet(const std::string& name, |
- int32 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 flags) |
+ : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {} |
- BooleanHistogram* tentative_histogram = |
- new BooleanHistogram(name, registered_ranges); |
+ protected: |
+ BucketRanges* CreateRanges() { |
Alexei Svitkine (slow)
2015/12/21 19:32:10
Can this class just extend LinearHistogram and avo
bcwhite
2015/12/22 20:50:37
Yes but it would involve changing the LinearHistog
|
+ 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; |
+HistogramBase* BooleanHistogram::FactoryGet(const std::string& name, |
+ int32 flags) { |
+ return Factory(name, flags).Build(); |
} |
HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32 flags) { |
@@ -739,6 +867,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; |
@@ -765,30 +899,46 @@ 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 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); |
+ } |
+ |
+ const std::vector<Sample>* custom_ranges_; |
+}; |
+ |
HistogramBase* CustomHistogram::FactoryGet( |
const std::string& name, |
const std::vector<Sample>& custom_ranges, |
int32 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( |
@@ -824,6 +974,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; |
@@ -886,22 +1049,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 |