diff options
author | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 21:52:44 +0000 |
---|---|---|
committer | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-11 21:52:44 +0000 |
commit | c50c21d07c17ade9e6a9b0a67eb505bea7bb8134 (patch) | |
tree | fc064c9b90ea4bbf97f016a0028e60d9b3cb9fb3 /base/metrics | |
parent | 02e33f8aea28a9ca82c87acb422d3fcceb1f9888 (diff) | |
download | chromium_src-c50c21d07c17ade9e6a9b0a67eb505bea7bb8134.zip chromium_src-c50c21d07c17ade9e6a9b0a67eb505bea7bb8134.tar.gz chromium_src-c50c21d07c17ade9e6a9b0a67eb505bea7bb8134.tar.bz2 |
Serialize/Deserialize support in HistogramBase
BUG=139612,167343
Review URL: https://chromiumcodereview.appspot.com/11682003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176449 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/metrics')
-rw-r--r-- | base/metrics/histogram.cc | 299 | ||||
-rw-r--r-- | base/metrics/histogram.h | 62 | ||||
-rw-r--r-- | base/metrics/histogram_base.cc | 45 | ||||
-rw-r--r-- | base/metrics/histogram_base.h | 28 | ||||
-rw-r--r-- | base/metrics/histogram_base_unittest.cc | 172 | ||||
-rw-r--r-- | base/metrics/histogram_unittest.cc | 124 | ||||
-rw-r--r-- | base/metrics/sample_map.cc | 5 | ||||
-rw-r--r-- | base/metrics/sample_map.h | 2 | ||||
-rw-r--r-- | base/metrics/sparse_histogram.cc | 42 | ||||
-rw-r--r-- | base/metrics/sparse_histogram.h | 22 | ||||
-rw-r--r-- | base/metrics/sparse_histogram_unittest.cc | 29 | ||||
-rw-r--r-- | base/metrics/statistics_recorder.h | 1 |
12 files changed, 622 insertions, 209 deletions
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index 795ea3a..d5da371 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc @@ -30,6 +30,54 @@ using std::vector; namespace base { +namespace { + +bool ReadHistogramArguments(PickleIterator* iter, + string* histogram_name, + int* flags, + int* declared_min, + int* declared_max, + uint64* bucket_count, + uint32* range_checksum) { + if (!iter->ReadString(histogram_name) || + !iter->ReadInt(flags) || + !iter->ReadInt(declared_min) || + !iter->ReadInt(declared_max) || + !iter->ReadUInt64(bucket_count) || + !iter->ReadUInt32(range_checksum)) { + DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name; + return false; + } + + // Since these fields may have come from an untrusted renderer, do additional + // checks above and beyond those in Histogram::Initialize() + if (*declared_max <= 0 || + *declared_min <= 0 || + *declared_max < *declared_min || + INT_MAX / sizeof(HistogramBase::Count) <= *bucket_count || + *bucket_count < 2) { + DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name; + return false; + } + + // We use the arguments to find or create the local version of the histogram + // in this process, so we need to clear the IPC flag. + DCHECK(*flags & HistogramBase::kIPCSerializationSourceFlag); + *flags &= ~HistogramBase::kIPCSerializationSourceFlag; + + return true; +} + +bool ValidateRangeChecksum(const HistogramBase& histogram, + uint32 range_checksum) { + const Histogram& casted_histogram = + static_cast<const Histogram&>(histogram); + + return casted_histogram.bucket_ranges()->checksum() == range_checksum; +} + +} // namespace + typedef HistogramBase::Count Count; typedef HistogramBase::Sample Sample; @@ -153,117 +201,6 @@ void Histogram::AddBoolean(bool value) { DCHECK(false); } -void Histogram::AddSamples(const HistogramSamples& samples) { - samples_->Add(samples); -} - -bool Histogram::AddSamplesFromPickle(PickleIterator* iter) { - return samples_->AddFromPickle(iter); -} - -// static -string Histogram::SerializeHistogramInfo(const Histogram& histogram, - const HistogramSamples& snapshot) { - DCHECK(histogram.bucket_ranges()->HasValidChecksum()); - - Pickle pickle; - pickle.WriteString(histogram.histogram_name()); - pickle.WriteInt(histogram.declared_min()); - pickle.WriteInt(histogram.declared_max()); - pickle.WriteUInt64(histogram.bucket_count()); - pickle.WriteUInt32(histogram.bucket_ranges()->checksum()); - pickle.WriteInt(histogram.GetHistogramType()); - pickle.WriteInt(histogram.flags()); - - histogram.SerializeRanges(&pickle); - - snapshot.Serialize(&pickle); - - return string(static_cast<const char*>(pickle.data()), pickle.size()); -} - -// static -bool Histogram::DeserializeHistogramInfo(const string& histogram_info) { - if (histogram_info.empty()) { - return false; - } - - Pickle pickle(histogram_info.data(), - static_cast<int>(histogram_info.size())); - string histogram_name; - int declared_min; - int declared_max; - uint64 bucket_count; - uint32 range_checksum; - int histogram_type; - int pickle_flags; - - PickleIterator iter(pickle); - if (!iter.ReadString(&histogram_name) || - !iter.ReadInt(&declared_min) || - !iter.ReadInt(&declared_max) || - !iter.ReadUInt64(&bucket_count) || - !iter.ReadUInt32(&range_checksum) || - !iter.ReadInt(&histogram_type) || - !iter.ReadInt(&pickle_flags)) { - DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; - return false; - } - - DCHECK(pickle_flags & kIPCSerializationSourceFlag); - // Since these fields may have come from an untrusted renderer, do additional - // checks above and beyond those in Histogram::Initialize() - if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || - INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) { - DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name; - return false; - } - - Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag); - - Histogram* render_histogram(NULL); - - if (histogram_type == HISTOGRAM) { - render_histogram = Histogram::FactoryGet( - histogram_name, declared_min, declared_max, bucket_count, flags); - } else if (histogram_type == LINEAR_HISTOGRAM) { - render_histogram = LinearHistogram::FactoryGet( - histogram_name, declared_min, declared_max, bucket_count, flags); - } else if (histogram_type == BOOLEAN_HISTOGRAM) { - render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); - } else if (histogram_type == CUSTOM_HISTOGRAM) { - vector<Sample> sample_ranges(bucket_count); - if (!CustomHistogram::DeserializeRanges(&iter, &sample_ranges)) { - DLOG(ERROR) << "Pickle error decoding ranges: " << histogram_name; - return false; - } - render_histogram = - CustomHistogram::FactoryGet(histogram_name, sample_ranges, flags); - } else { - DLOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " - << histogram_type; - return false; - } - - DCHECK_EQ(render_histogram->declared_min(), declared_min); - DCHECK_EQ(render_histogram->declared_max(), declared_max); - DCHECK_EQ(render_histogram->bucket_count(), bucket_count); - DCHECK_EQ(render_histogram->GetHistogramType(), histogram_type); - - if (render_histogram->bucket_ranges()->checksum() != range_checksum) { - return false; - } - - if (render_histogram->flags() & kIPCSerializationSourceFlag) { - DVLOG(1) << "Single process mode, histogram observed and not copied: " - << histogram_name; - return true; - } - - DCHECK_EQ(flags & render_histogram->flags(), flags); - return render_histogram->AddSamplesFromPickle(&iter); -} - // static const int Histogram::kCommonRaceBasedCountMismatch = 5; @@ -363,6 +300,14 @@ scoped_ptr<HistogramSamples> Histogram::SnapshotSamples() const { return SnapshotSampleVector().PassAs<HistogramSamples>(); } +void Histogram::AddSamples(const HistogramSamples& samples) { + samples_->Add(samples); +} + +bool Histogram::AddSamplesFromPickle(PickleIterator* iter) { + return samples_->AddFromPickle(iter); +} + // The following methods provide a graphical histogram display. void Histogram::WriteHTMLGraph(string* output) const { // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. @@ -375,6 +320,16 @@ void Histogram::WriteAscii(string* output) const { WriteAsciiImpl(true, "\n", output); } +bool Histogram::SerializeInfoImpl(Pickle* pickle) const { + DCHECK(bucket_ranges()->HasValidChecksum()); + return pickle->WriteString(histogram_name()) && + pickle->WriteInt(flags()) && + pickle->WriteInt(declared_min()) && + pickle->WriteInt(declared_max()) && + pickle->WriteUInt64(bucket_count()) && + pickle->WriteUInt32(bucket_ranges()->checksum()); +} + Histogram::Histogram(const string& name, Sample minimum, Sample maximum, @@ -397,10 +352,6 @@ Histogram::~Histogram() { } } -bool Histogram::SerializeRanges(Pickle* pickle) const { - return true; -} - bool Histogram::PrintEmptyBucket(size_t index) const { return true; } @@ -431,6 +382,31 @@ const string Histogram::GetAsciiBucketRange(size_t i) const { //------------------------------------------------------------------------------ // Private methods +// static +HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) { + string histogram_name; + int flags; + int declared_min; + int declared_max; + uint64 bucket_count; + uint32 range_checksum; + + if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, + &declared_max, &bucket_count, &range_checksum)) { + return NULL; + } + + // Find or create the local version of the histogram in this process. + HistogramBase* histogram = Histogram::FactoryGet( + histogram_name, declared_min, declared_max, bucket_count, flags); + + if (!ValidateRangeChecksum(*histogram, range_checksum)) { + // The serialized histogram might be corrupted. + return NULL; + } + return histogram; +} + scoped_ptr<SampleVector> Histogram::SnapshotSampleVector() const { scoped_ptr<SampleVector> samples(new SampleVector(bucket_ranges())); samples->Add(*samples_); @@ -710,6 +686,29 @@ void LinearHistogram::InitializeBucketRanges(Sample minimum, ranges->ResetChecksum(); } +// static +HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) { + string histogram_name; + int flags; + int declared_min; + int declared_max; + uint64 bucket_count; + uint32 range_checksum; + + if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, + &declared_max, &bucket_count, &range_checksum)) { + return NULL; + } + + HistogramBase* histogram = LinearHistogram::FactoryGet( + histogram_name, declared_min, declared_max, bucket_count, flags); + if (!ValidateRangeChecksum(*histogram, range_checksum)) { + // The serialized histogram might be corrupted. + return NULL; + } + return histogram; +} + //------------------------------------------------------------------------------ // This section provides implementation for BooleanHistogram. //------------------------------------------------------------------------------ @@ -750,6 +749,28 @@ BooleanHistogram::BooleanHistogram(const string& name, const BucketRanges* ranges) : LinearHistogram(name, 1, 2, 3, ranges) {} +HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) { + string histogram_name; + int flags; + int declared_min; + int declared_max; + uint64 bucket_count; + uint32 range_checksum; + + if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, + &declared_max, &bucket_count, &range_checksum)) { + return NULL; + } + + HistogramBase* histogram = BooleanHistogram::FactoryGet( + histogram_name, flags); + if (!ValidateRangeChecksum(*histogram, range_checksum)) { + // The serialized histogram might be corrupted. + return NULL; + } + return histogram; +} + //------------------------------------------------------------------------------ // CustomHistogram: //------------------------------------------------------------------------------ @@ -809,26 +830,52 @@ CustomHistogram::CustomHistogram(const string& name, ranges->size() - 1, ranges) {} -bool CustomHistogram::SerializeRanges(Pickle* pickle) const { - for (size_t i = 0; i < bucket_ranges()->size(); ++i) { +bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const { + if (!Histogram::SerializeInfoImpl(pickle)) + return false; + + // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't + // write them. + for (size_t i = 1; i < bucket_ranges()->size() - 1; ++i) { if (!pickle->WriteInt(bucket_ranges()->range(i))) return false; } return true; } +double CustomHistogram::GetBucketSize(Count current, size_t i) const { + return 1; +} + // static -bool CustomHistogram::DeserializeRanges( - PickleIterator* iter, vector<Sample>* ranges) { - for (size_t i = 0; i < ranges->size(); ++i) { - if (!iter->ReadInt(&(*ranges)[i])) - return false; +HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) { + string histogram_name; + int flags; + int declared_min; + int declared_max; + uint64 bucket_count; + uint32 range_checksum; + + if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, + &declared_max, &bucket_count, &range_checksum)) { + return NULL; } - return true; -} -double CustomHistogram::GetBucketSize(Count current, size_t i) const { - return 1; + // First and last ranges are not serialized. + vector<Sample> sample_ranges(bucket_count - 1); + + for (size_t i = 0; i < sample_ranges.size(); ++i) { + if (!iter->ReadInt(&sample_ranges[i])) + return NULL; + } + + HistogramBase* histogram = CustomHistogram::FactoryGet( + histogram_name, sample_ranges, flags); + if (!ValidateRangeChecksum(*histogram, range_checksum)) { + // The serialized histogram might be corrupted. + return NULL; + } + return histogram; } // static diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 02a975c..ed8de6f 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h @@ -375,11 +375,6 @@ class BASE_EXPORT Histogram : public HistogramBase { NEVER_EXCEEDED_VALUE = 0x10 }; - struct DescriptionPair { - Sample sample; - const char* description; // Null means end of a list of pairs. - }; - //---------------------------------------------------------------------------- // For a valid histogram, input should follow these restrictions: // minimum > 0 (if a minimum below 1 is specified, it will implicitly be @@ -418,25 +413,6 @@ class BASE_EXPORT Histogram : public HistogramBase { Add(static_cast<int>(time.InMilliseconds())); } - void AddSamples(const HistogramSamples& samples); - bool AddSamplesFromPickle(PickleIterator* iter); - - // Convenience methods for serializing/deserializing the histograms. - // Histograms from Renderer process are serialized and sent to the browser. - // Browser process reconstructs the histogram from the pickled version - // accumulates the browser-side shadow copy of histograms (that mirror - // histograms created in the renderer). - - // Serialize the given snapshot of a Histogram into a String. Uses - // Pickle class to flatten the object. - static std::string SerializeHistogramInfo(const Histogram& histogram, - const HistogramSamples& snapshot); - - // The following method accepts a list of pickled histograms and - // builds a histogram and updates shadow copy of histogram data in the - // browser process. - static bool DeserializeHistogramInfo(const std::string& histogram_info); - // This constant if for FindCorruption. Since snapshots of histograms are // taken asynchronously relative to sampling, and our counting code currently // does not prevent race conditions, it is pretty likely that we'll catch a @@ -480,6 +456,8 @@ class BASE_EXPORT Histogram : public HistogramBase { size_t bucket_count) const OVERRIDE; virtual void Add(Sample value) OVERRIDE; virtual scoped_ptr<HistogramSamples> SnapshotSamples() const OVERRIDE; + virtual void AddSamples(const HistogramSamples& samples) OVERRIDE; + virtual bool AddSamplesFromPickle(PickleIterator* iter) OVERRIDE; virtual void WriteHTMLGraph(std::string* output) const OVERRIDE; virtual void WriteAscii(std::string* output) const OVERRIDE; @@ -494,11 +472,8 @@ class BASE_EXPORT Histogram : public HistogramBase { virtual ~Histogram(); - // Serialize the histogram's ranges to |*pickle|, returning true on success. - // Most subclasses can leave this no-op implementation, but some will want to - // override it, especially if the ranges cannot be re-derived from other - // serialized parameters. - virtual bool SerializeRanges(Pickle* pickle) const; + // HistogramBase implementation: + virtual bool SerializeInfoImpl(Pickle* pickle) const OVERRIDE; // Method to override to skip the display of the i'th bucket if it's empty. virtual bool PrintEmptyBucket(size_t index) const; @@ -522,6 +497,10 @@ class BASE_EXPORT Histogram : public HistogramBase { friend class StatisticsRecorder; // To allow it to delete duplicates. friend class StatisticsRecorderTest; + friend BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo( + PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(PickleIterator* iter); + // Implementation of SnapshotSamples function. scoped_ptr<SampleVector> SnapshotSampleVector() const; @@ -596,6 +575,11 @@ class BASE_EXPORT LinearHistogram : public Histogram { size_t bucket_count, int32 flags); + struct DescriptionPair { + Sample sample; + const char* description; // Null means end of a list of pairs. + }; + // Create a LinearHistogram and store a list of number/text values for use in // writing the histogram graph. // |descriptions| can be NULL, which means no special descriptions to set. If @@ -635,6 +619,10 @@ class BASE_EXPORT LinearHistogram : public Histogram { virtual bool PrintEmptyBucket(size_t index) const OVERRIDE; private: + friend BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo( + PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(PickleIterator* iter); + // For some ranges, we store a printable description of a bucket range. // If there is no desciption, then GetAsciiBucketRange() uses parent class // to provide a description. @@ -658,6 +646,10 @@ class BASE_EXPORT BooleanHistogram : public LinearHistogram { private: BooleanHistogram(const std::string& name, const BucketRanges* ranges); + friend BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo( + PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(PickleIterator* iter); + DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); }; @@ -685,20 +677,20 @@ class BASE_EXPORT CustomHistogram : public Histogram { // TODO(kaiwang): Change name to ArrayToCustomEnumRanges. static std::vector<Sample> ArrayToCustomRanges(const Sample* values, size_t num_values); - - // Helper for deserializing CustomHistograms. |*ranges| should already be - // correctly sized before this call. Return true on success. - static bool DeserializeRanges(PickleIterator* iter, - std::vector<Sample>* ranges); protected: CustomHistogram(const std::string& name, const BucketRanges* ranges); - virtual bool SerializeRanges(Pickle* pickle) const OVERRIDE; + // HistogramBase implementation: + virtual bool SerializeInfoImpl(Pickle* pickle) const OVERRIDE; virtual double GetBucketSize(Count current, size_t i) const OVERRIDE; private: + friend BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo( + PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(PickleIterator* iter); + static bool ValidateCustomRanges(const std::vector<Sample>& custom_ranges); static BucketRanges* CreateBucketRangesFromCustomRanges( const std::vector<Sample>& custom_ranges); diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc index bcfb57f..6396132 100644 --- a/base/metrics/histogram_base.cc +++ b/base/metrics/histogram_base.cc @@ -9,6 +9,10 @@ #include "base/logging.h" #include "base/json/json_string_value_serializer.h" #include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_samples.h" +#include "base/metrics/sparse_histogram.h" +#include "base/pickle.h" #include "base/values.h" namespace base { @@ -31,6 +35,41 @@ std::string HistogramTypeToString(HistogramType type) { return "UNKNOWN"; } +HistogramBase* DeserializeHistogramInfo(PickleIterator* iter) { + int type; + if (!iter->ReadInt(&type)) + return NULL; + + switch (type) { + case HISTOGRAM: + return Histogram::DeserializeInfoImpl(iter); + case LINEAR_HISTOGRAM: + return LinearHistogram::DeserializeInfoImpl(iter); + case BOOLEAN_HISTOGRAM: + return BooleanHistogram::DeserializeInfoImpl(iter); + case CUSTOM_HISTOGRAM: + return CustomHistogram::DeserializeInfoImpl(iter); + case SPARSE_HISTOGRAM: + return SparseHistogram::DeserializeInfoImpl(iter); + default: + return NULL; + } +} + +void DeserializeHistogramAndAddSamples(PickleIterator* iter) { + HistogramBase* histogram = DeserializeHistogramInfo(iter); + if (!histogram) + return; + + if (histogram->flags() & base::HistogramBase::kIPCSerializationSourceFlag) { + DVLOG(1) << "Single process mode, histogram observed and not copied: " + << histogram->histogram_name(); + return; + } + histogram->AddSamplesFromPickle(iter); +} + + const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; HistogramBase::HistogramBase(const std::string& name) @@ -47,6 +86,12 @@ void HistogramBase::ClearFlags(int32 flags) { flags_ &= ~flags; } +bool HistogramBase::SerializeInfo(Pickle* pickle) const { + if (!pickle->WriteInt(GetHistogramType())) + return false; + return SerializeInfoImpl(pickle); +} + void HistogramBase::WriteJSON(std::string* output) const { Count count; scoped_ptr<ListValue> buckets(new ListValue()); diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h index 302795a..69e1437 100644 --- a/base/metrics/histogram_base.h +++ b/base/metrics/histogram_base.h @@ -11,9 +11,13 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +class Pickle; +class PickleIterator; + namespace base { class DictionaryValue; +class HistogramBase; class HistogramSamples; class ListValue; @@ -32,6 +36,15 @@ enum BASE_EXPORT HistogramType { std::string HistogramTypeToString(HistogramType type); +// Create or find existing histogram that matches the pickled info. +// Returns NULL if the pickled data has problems. +BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo( + PickleIterator* iter); + +// Create or find existing histogram and add the samples from pickle. +// Silently returns when seeing any data problem in the pickle. +BASE_EXPORT void DeserializeHistogramAndAddSamples(PickleIterator* iter); + //////////////////////////////////////////////////////////////////////////////// class BASE_EXPORT HistogramBase { @@ -69,14 +82,22 @@ class BASE_EXPORT HistogramBase { virtual HistogramType GetHistogramType() const = 0; // Whether the histogram has construction arguments as parameters specified. - // For histograms that don't have the concept of minimum, maximum or - // bucket_count, this function always returns false. + // For histograms that don't have the concept of |minimum|, |maximum| or + // |bucket_count|, this function always returns false. virtual bool HasConstructionArguments(Sample minimum, Sample maximum, size_t bucket_count) const = 0; virtual void Add(Sample value) = 0; + virtual void AddSamples(const HistogramSamples& samples) = 0; + virtual bool AddSamplesFromPickle(PickleIterator* iter) = 0; + + // Serialize the histogram info into |pickle|. + // Note: This only serializes the construction arguments of the histogram, but + // does not serialize the samples. + bool SerializeInfo(Pickle* pickle) const; + // Snapshot the current complete set of sample data. // Override with atomic/locked snapshot if needed. virtual scoped_ptr<HistogramSamples> SnapshotSamples() const = 0; @@ -91,6 +112,9 @@ class BASE_EXPORT HistogramBase { void WriteJSON(std::string* output) const; protected: + // Subclasses should implement this function to make SerializeInfo work. + virtual bool SerializeInfoImpl(Pickle* pickle) const = 0; + // Writes information about the construction parameters in |params|. virtual void GetParameters(DictionaryValue* params) const = 0; diff --git a/base/metrics/histogram_base_unittest.cc b/base/metrics/histogram_base_unittest.cc new file mode 100644 index 0000000..4944f31 --- /dev/null +++ b/base/metrics/histogram_base_unittest.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2012 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 <vector> + +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/sparse_histogram.h" +#include "base/metrics/statistics_recorder.h" +#include "base/pickle.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +class HistogramBaseTest : public testing::Test { + protected: + HistogramBaseTest() { + // Each test will have a clean state (no Histogram / BucketRanges + // registered). + statistics_recorder_ = NULL; + ResetStatisticsRecorder(); + } + + virtual ~HistogramBaseTest() { + delete statistics_recorder_; + } + + void ResetStatisticsRecorder() { + delete statistics_recorder_; + statistics_recorder_ = new StatisticsRecorder(); + } + + private: + StatisticsRecorder* statistics_recorder_; +}; + +TEST_F(HistogramBaseTest, DeserializeHistogram) { + HistogramBase* histogram = Histogram::FactoryGet( + "TestHistogram", 1, 1000, 10, + (HistogramBase::kUmaTargetedHistogramFlag | + HistogramBase::kIPCSerializationSourceFlag)); + + Pickle pickle; + ASSERT_TRUE(histogram->SerializeInfo(&pickle)); + + PickleIterator iter(pickle); + HistogramBase* deserialized = DeserializeHistogramInfo(&iter); + EXPECT_EQ(histogram, deserialized); + + ResetStatisticsRecorder(); + + PickleIterator iter2(pickle); + deserialized = DeserializeHistogramInfo(&iter2); + EXPECT_TRUE(deserialized); + EXPECT_NE(histogram, deserialized); + EXPECT_EQ("TestHistogram", deserialized->histogram_name()); + EXPECT_TRUE(deserialized->HasConstructionArguments(1, 1000, 10)); + + // kIPCSerializationSourceFlag will be cleared. + EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, deserialized->flags()); +} + +TEST_F(HistogramBaseTest, DeserializeHistogramAndAddSamples) { + HistogramBase* histogram = Histogram::FactoryGet( + "TestHistogram", 1, 1000, 10, HistogramBase::kIPCSerializationSourceFlag); + histogram->Add(1); + histogram->Add(10); + histogram->Add(100); + histogram->Add(1000); + + Pickle pickle; + ASSERT_TRUE(histogram->SerializeInfo(&pickle)); + histogram->SnapshotSamples()->Serialize(&pickle); + + PickleIterator iter(pickle); + DeserializeHistogramAndAddSamples(&iter); + + // The histogram has kIPCSerializationSourceFlag. So samples will be ignored. + scoped_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples()); + EXPECT_EQ(1, snapshot->GetCount(1)); + EXPECT_EQ(1, snapshot->GetCount(10)); + EXPECT_EQ(1, snapshot->GetCount(100)); + EXPECT_EQ(1, snapshot->GetCount(1000)); + + // Clear kIPCSerializationSourceFlag to emulate multi-process usage. + histogram->ClearFlags(HistogramBase::kIPCSerializationSourceFlag); + PickleIterator iter2(pickle); + DeserializeHistogramAndAddSamples(&iter2); + + scoped_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples()); + EXPECT_EQ(2, snapshot2->GetCount(1)); + EXPECT_EQ(2, snapshot2->GetCount(10)); + EXPECT_EQ(2, snapshot2->GetCount(100)); + EXPECT_EQ(2, snapshot2->GetCount(1000)); +} + +TEST_F(HistogramBaseTest, DeserializeLinearHistogram) { + HistogramBase* histogram = LinearHistogram::FactoryGet( + "TestHistogram", 1, 1000, 10, + HistogramBase::kIPCSerializationSourceFlag); + + Pickle pickle; + ASSERT_TRUE(histogram->SerializeInfo(&pickle)); + + PickleIterator iter(pickle); + HistogramBase* deserialized = DeserializeHistogramInfo(&iter); + EXPECT_EQ(histogram, deserialized); + + ResetStatisticsRecorder(); + + PickleIterator iter2(pickle); + deserialized = DeserializeHistogramInfo(&iter2); + EXPECT_TRUE(deserialized); + EXPECT_NE(histogram, deserialized); + EXPECT_EQ("TestHistogram", deserialized->histogram_name()); + EXPECT_TRUE(deserialized->HasConstructionArguments(1, 1000, 10)); + EXPECT_EQ(0, deserialized->flags()); +} + +TEST_F(HistogramBaseTest, DeserializeBooleanHistogram) { + HistogramBase* histogram = BooleanHistogram::FactoryGet( + "TestHistogram", HistogramBase::kIPCSerializationSourceFlag); + + Pickle pickle; + ASSERT_TRUE(histogram->SerializeInfo(&pickle)); + + PickleIterator iter(pickle); + HistogramBase* deserialized = DeserializeHistogramInfo(&iter); + EXPECT_EQ(histogram, deserialized); + + ResetStatisticsRecorder(); + + PickleIterator iter2(pickle); + deserialized = DeserializeHistogramInfo(&iter2); + EXPECT_TRUE(deserialized); + EXPECT_NE(histogram, deserialized); + EXPECT_EQ("TestHistogram", deserialized->histogram_name()); + EXPECT_TRUE(deserialized->HasConstructionArguments(1, 2, 3)); + EXPECT_EQ(0, deserialized->flags()); +} + +TEST_F(HistogramBaseTest, DeserializeCustomHistogram) { + std::vector<HistogramBase::Sample> ranges; + ranges.push_back(13); + ranges.push_back(5); + ranges.push_back(9); + + HistogramBase* histogram = CustomHistogram::FactoryGet( + "TestHistogram", ranges, HistogramBase::kIPCSerializationSourceFlag); + + Pickle pickle; + ASSERT_TRUE(histogram->SerializeInfo(&pickle)); + + PickleIterator iter(pickle); + HistogramBase* deserialized = DeserializeHistogramInfo(&iter); + EXPECT_EQ(histogram, deserialized); + + ResetStatisticsRecorder(); + + PickleIterator iter2(pickle); + deserialized = DeserializeHistogramInfo(&iter2); + EXPECT_TRUE(deserialized); + EXPECT_NE(histogram, deserialized); + EXPECT_EQ("TestHistogram", deserialized->histogram_name()); + EXPECT_TRUE(deserialized->HasConstructionArguments(5, 13, 4)); + EXPECT_EQ(0, deserialized->flags()); +} + +// TODO(kaiwang): Add SparseHistogram test. + +} // namespace base diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc index 9fb9422..ee4e024 100644 --- a/base/metrics/histogram_unittest.cc +++ b/base/metrics/histogram_unittest.cc @@ -14,6 +14,7 @@ #include "base/metrics/histogram.h" #include "base/metrics/sample_vector.h" #include "base/metrics/statistics_recorder.h" +#include "base/pickle.h" #include "base/time.h" #include "testing/gtest/include/gtest/gtest.h" @@ -49,18 +50,18 @@ class HistogramTest : public testing::Test { TEST_F(HistogramTest, BasicTest) { // Try basic construction Histogram* histogram(Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, Histogram::kNoFlags)); + "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags)); EXPECT_NE(reinterpret_cast<Histogram*>(NULL), histogram); Histogram* linear_histogram(LinearHistogram::FactoryGet( - "TestLinearHistogram", 1, 1000, 10, Histogram::kNoFlags)); + "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags)); EXPECT_NE(reinterpret_cast<Histogram*>(NULL), linear_histogram); vector<int> custom_ranges; custom_ranges.push_back(1); custom_ranges.push_back(5); Histogram* custom_histogram(CustomHistogram::FactoryGet( - "TestCustomHistogram", custom_ranges, Histogram::kNoFlags)); + "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags)); EXPECT_NE(reinterpret_cast<Histogram*>(NULL), custom_histogram); // Use standard macros (but with fixed samples) @@ -79,7 +80,7 @@ TEST_F(HistogramTest, NameMatchTest) { HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10); HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10); Histogram* histogram(LinearHistogram::FactoryGet( - "DuplicatedHistogram", 1, 101, 102, Histogram::kNoFlags)); + "DuplicatedHistogram", 1, 101, 102, HistogramBase::kNoFlags)); scoped_ptr<SampleVector> samples = histogram->SnapshotSampleVector(); EXPECT_EQ(2, samples->TotalCount()); EXPECT_EQ(2, samples->GetCountAtIndex(10)); @@ -99,7 +100,7 @@ TEST_F(HistogramTest, ExponentialRangesTest) { // Check the corresponding Histogram will use the correct ranges. Histogram* histogram(Histogram::FactoryGet( - "Histogram", 1, 64, 8, Histogram::kNoFlags)); + "Histogram", 1, 64, 8, HistogramBase::kNoFlags)); EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); // When bucket count is limited, exponential ranges will partially look like @@ -126,7 +127,7 @@ TEST_F(HistogramTest, ExponentialRangesTest) { // Check the corresponding Histogram will use the correct ranges. Histogram* histogram2(Histogram::FactoryGet( - "Histogram2", 1, 32, 15, Histogram::kNoFlags)); + "Histogram2", 1, 32, 15, HistogramBase::kNoFlags)); EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); } @@ -139,7 +140,7 @@ TEST_F(HistogramTest, LinearRangesTest) { EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8)); // The correspoding LinearHistogram should use the correct ranges. Histogram* histogram( - LinearHistogram::FactoryGet("Linear", 1, 7, 8, Histogram::kNoFlags)); + LinearHistogram::FactoryGet("Linear", 1, 7, 8, HistogramBase::kNoFlags)); EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); // Linear ranges are not divisible. @@ -153,7 +154,7 @@ TEST_F(HistogramTest, LinearRangesTest) { EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(5)); // The correspoding LinearHistogram should use the correct ranges. Histogram* histogram2( - LinearHistogram::FactoryGet("Linear2", 1, 6, 5, Histogram::kNoFlags)); + LinearHistogram::FactoryGet("Linear2", 1, 6, 5, HistogramBase::kNoFlags)); EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); } @@ -176,7 +177,7 @@ TEST_F(HistogramTest, CustomHistogramTest) { custom_ranges.push_back(1); custom_ranges.push_back(2); Histogram* histogram = CustomHistogram::FactoryGet( - "TestCustomHistogram1", custom_ranges, Histogram::kNoFlags); + "TestCustomHistogram1", custom_ranges, HistogramBase::kNoFlags); const BucketRanges* ranges = histogram->bucket_ranges(); ASSERT_EQ(4u, ranges->size()); EXPECT_EQ(0, ranges->range(0)); // Auto added. @@ -189,7 +190,7 @@ TEST_F(HistogramTest, CustomHistogramTest) { custom_ranges.push_back(2); custom_ranges.push_back(1); histogram = CustomHistogram::FactoryGet( - "TestCustomHistogram2", custom_ranges, Histogram::kNoFlags); + "TestCustomHistogram2", custom_ranges, HistogramBase::kNoFlags); ranges = histogram->bucket_ranges(); ASSERT_EQ(4u, ranges->size()); EXPECT_EQ(0, ranges->range(0)); @@ -203,7 +204,7 @@ TEST_F(HistogramTest, CustomHistogramTest) { custom_ranges.push_back(1); custom_ranges.push_back(4); histogram = CustomHistogram::FactoryGet( - "TestCustomHistogram3", custom_ranges, Histogram::kNoFlags); + "TestCustomHistogram3", custom_ranges, HistogramBase::kNoFlags); ranges = histogram->bucket_ranges(); ASSERT_EQ(4u, ranges->size()); EXPECT_EQ(0, ranges->range(0)); @@ -222,7 +223,7 @@ TEST_F(HistogramTest, CustomHistogramWithOnly2Buckets) { custom_ranges.push_back(4); Histogram* histogram = CustomHistogram::FactoryGet( - "2BucketsCustomHistogram", custom_ranges, Histogram::kNoFlags); + "2BucketsCustomHistogram", custom_ranges, HistogramBase::kNoFlags); const BucketRanges* ranges = histogram->bucket_ranges(); ASSERT_EQ(3u, ranges->size()); EXPECT_EQ(0, ranges->range(0)); @@ -234,7 +235,7 @@ TEST_F(HistogramTest, CustomHistogramWithOnly2Buckets) { TEST_F(HistogramTest, BoundsTest) { const size_t kBucketCount = 50; Histogram* histogram(Histogram::FactoryGet( - "Bounded", 10, 100, kBucketCount, Histogram::kNoFlags)); + "Bounded", 10, 100, kBucketCount, HistogramBase::kNoFlags)); // Put two samples "out of bounds" above and below. histogram->Add(5); @@ -256,8 +257,9 @@ TEST_F(HistogramTest, BoundsTest) { custom_ranges.push_back(10); custom_ranges.push_back(50); custom_ranges.push_back(100); - Histogram* test_custom_histogram(CustomHistogram::FactoryGet( - "TestCustomRangeBoundedHistogram", custom_ranges, Histogram::kNoFlags)); + Histogram* test_custom_histogram = CustomHistogram::FactoryGet( + "TestCustomRangeBoundedHistogram", custom_ranges, + HistogramBase::kNoFlags); // Put two samples "out of bounds" above and below. test_custom_histogram->Add(5); @@ -279,7 +281,7 @@ TEST_F(HistogramTest, BoundsTest) { // Check to be sure samples land as expected is "correct" buckets. TEST_F(HistogramTest, BucketPlacementTest) { Histogram* histogram(Histogram::FactoryGet( - "Histogram", 1, 64, 8, Histogram::kNoFlags)); + "Histogram", 1, 64, 8, HistogramBase::kNoFlags)); // Add i+1 samples to the i'th bucket. histogram->Add(0); @@ -298,7 +300,7 @@ TEST_F(HistogramTest, BucketPlacementTest) { TEST_F(HistogramTest, CorruptSampleCounts) { Histogram* histogram(Histogram::FactoryGet( - "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file. + "Histogram", 1, 64, 8, HistogramBase::kNoFlags)); // As per header file. // Add some samples. histogram->Add(20); @@ -325,7 +327,7 @@ TEST_F(HistogramTest, CorruptSampleCounts) { TEST_F(HistogramTest, CorruptBucketBounds) { Histogram* histogram(Histogram::FactoryGet( - "Histogram", 1, 64, 8, Histogram::kNoFlags)); // As per header file. + "Histogram", 1, 64, 8, HistogramBase::kNoFlags)); // As per header file. scoped_ptr<SampleVector> snapshot = histogram->SnapshotSampleVector(); EXPECT_EQ(Histogram::NO_INCONSISTENCIES, @@ -357,6 +359,81 @@ TEST_F(HistogramTest, CorruptBucketBounds) { bucket_ranges->set_range(4, bucket_ranges->range(4) + 1); } +TEST_F(HistogramTest, HistogramSerializeInfo) { + Histogram* histogram = Histogram::FactoryGet( + "Histogram", 1, 64, 8, HistogramBase::kIPCSerializationSourceFlag); + + Pickle pickle; + histogram->SerializeInfo(&pickle); + + PickleIterator iter(pickle); + + int type; + EXPECT_TRUE(iter.ReadInt(&type)); + EXPECT_EQ(HISTOGRAM, type); + + std::string name; + EXPECT_TRUE(iter.ReadString(&name)); + EXPECT_EQ("Histogram", name); + + int flag; + EXPECT_TRUE(iter.ReadInt(&flag)); + EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, flag); + + int min; + EXPECT_TRUE(iter.ReadInt(&min)); + EXPECT_EQ(1, min); + + int max; + EXPECT_TRUE(iter.ReadInt(&max)); + EXPECT_EQ(64, max); + + int64 bucket_count; + EXPECT_TRUE(iter.ReadInt64(&bucket_count)); + EXPECT_EQ(8, bucket_count); + + uint32 checksum; + EXPECT_TRUE(iter.ReadUInt32(&checksum)); + EXPECT_EQ(histogram->bucket_ranges()->checksum(), checksum); + + // No more data in the pickle. + EXPECT_FALSE(iter.SkipBytes(1)); +} + +TEST_F(HistogramTest, CustomHistogramSerializeInfo) { + vector<int> custom_ranges; + custom_ranges.push_back(10); + custom_ranges.push_back(100); + + Histogram* custom_histogram = CustomHistogram::FactoryGet( + "TestCustomRangeBoundedHistogram", + custom_ranges, + HistogramBase::kNoFlags); + Pickle pickle; + custom_histogram->SerializeInfo(&pickle); + + // Validate the pickle. + PickleIterator iter(pickle); + + int i; + std::string s; + int64 bucket_count; + uint32 ui32; + EXPECT_TRUE(iter.ReadInt(&i) && iter.ReadString(&s) && iter.ReadInt(&i) && + iter.ReadInt(&i) && iter.ReadInt(&i) && + iter.ReadInt64(&bucket_count) && iter.ReadUInt32(&ui32)); + EXPECT_EQ(3, bucket_count); + + int range; + EXPECT_TRUE(iter.ReadInt(&range)); + EXPECT_EQ(10, range); + EXPECT_TRUE(iter.ReadInt(&range)); + EXPECT_EQ(100, range); + + // No more data in the pickle. + EXPECT_FALSE(iter.SkipBytes(1)); +} + #if GTEST_HAS_DEATH_TEST // For Histogram, LinearHistogram and CustomHistogram, the minimum for a // declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX - @@ -364,13 +441,14 @@ TEST_F(HistogramTest, CorruptBucketBounds) { // those limits. This is for backwards compatibility. TEST(HistogramDeathTest, BadRangesTest) { Histogram* histogram = Histogram::FactoryGet( - "BadRanges", 0, HistogramBase::kSampleType_MAX, 8, Histogram::kNoFlags); + "BadRanges", 0, HistogramBase::kSampleType_MAX, 8, + HistogramBase::kNoFlags); EXPECT_EQ(1, histogram->declared_min()); EXPECT_EQ(HistogramBase::kSampleType_MAX - 1, histogram->declared_max()); Histogram* linear_histogram = LinearHistogram::FactoryGet( "BadRangesLinear", 0, HistogramBase::kSampleType_MAX, 8, - Histogram::kNoFlags); + HistogramBase::kNoFlags); EXPECT_EQ(1, linear_histogram->declared_min()); EXPECT_EQ(HistogramBase::kSampleType_MAX - 1, linear_histogram->declared_max()); @@ -379,7 +457,7 @@ TEST(HistogramDeathTest, BadRangesTest) { custom_ranges.push_back(0); custom_ranges.push_back(5); Histogram* custom_histogram1 = CustomHistogram::FactoryGet( - "BadRangesCustom", custom_ranges, Histogram::kNoFlags); + "BadRangesCustom", custom_ranges, HistogramBase::kNoFlags); const BucketRanges* ranges = custom_histogram1->bucket_ranges(); ASSERT_EQ(3u, ranges->size()); EXPECT_EQ(0, ranges->range(0)); @@ -389,14 +467,14 @@ TEST(HistogramDeathTest, BadRangesTest) { // CustomHistogram does not accepts kSampleType_MAX as range. custom_ranges.push_back(HistogramBase::kSampleType_MAX); EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges, - Histogram::kNoFlags), + HistogramBase::kNoFlags), ""); // CustomHistogram needs at least 1 valid range. custom_ranges.clear(); custom_ranges.push_back(0); EXPECT_DEATH(CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges, - Histogram::kNoFlags), + HistogramBase::kNoFlags), ""); } #endif diff --git a/base/metrics/sample_map.cc b/base/metrics/sample_map.cc index 9bd78e7..42468cb 100644 --- a/base/metrics/sample_map.cc +++ b/base/metrics/sample_map.cc @@ -44,11 +44,6 @@ scoped_ptr<SampleCountIterator> SampleMap::Iterator() const { return scoped_ptr<SampleCountIterator>(new SampleMapIterator(sample_counts_)); } -void SampleMap::ResetRedundantCount(Count count) { - IncreaseRedundantCount(-redundant_count()); - IncreaseRedundantCount(count); -} - bool SampleMap::AddSubtractImpl(SampleCountIterator* iter, HistogramSamples::Operator op) { Sample min; diff --git a/base/metrics/sample_map.h b/base/metrics/sample_map.h index 65a31f1..22c391bb 100644 --- a/base/metrics/sample_map.h +++ b/base/metrics/sample_map.h @@ -30,8 +30,6 @@ class BASE_EXPORT_PRIVATE SampleMap : public HistogramSamples { virtual HistogramBase::Count TotalCount() const OVERRIDE; virtual scoped_ptr<SampleCountIterator> Iterator() const OVERRIDE; - void ResetRedundantCount(HistogramBase::Count count); - protected: virtual bool AddSubtractImpl( SampleCountIterator* iter, diff --git a/base/metrics/sparse_histogram.cc b/base/metrics/sparse_histogram.cc index 169037d..c64f7cb 100644 --- a/base/metrics/sparse_histogram.cc +++ b/base/metrics/sparse_histogram.cc @@ -6,6 +6,7 @@ #include "base/metrics/sample_map.h" #include "base/metrics/statistics_recorder.h" +#include "base/pickle.h" #include "base/synchronization/lock.h" using std::map; @@ -39,23 +40,27 @@ bool SparseHistogram::HasConstructionArguments(Sample minimum, void SparseHistogram::Add(Sample value) { base::AutoLock auto_lock(lock_); - sample_counts_[value]++; - redundant_count_ += 1; + samples_.Accumulate(value, 1); } scoped_ptr<HistogramSamples> SparseHistogram::SnapshotSamples() const { scoped_ptr<SampleMap> snapshot(new SampleMap()); base::AutoLock auto_lock(lock_); - for(map<Sample, Count>::const_iterator it = sample_counts_.begin(); - it != sample_counts_.end(); - ++it) { - snapshot->Accumulate(it->first, it->second); - } - snapshot->ResetRedundantCount(redundant_count_); + snapshot->Add(samples_); return snapshot.PassAs<HistogramSamples>(); } +void SparseHistogram::AddSamples(const HistogramSamples& samples) { + base::AutoLock auto_lock(lock_); + samples_.Add(samples); +} + +bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) { + base::AutoLock auto_lock(lock_); + return samples_.AddFromPickle(iter); +} + void SparseHistogram::WriteHTMLGraph(string* output) const { // TODO(kaiwang): Implement. } @@ -64,9 +69,26 @@ void SparseHistogram::WriteAscii(string* output) const { // TODO(kaiwang): Implement. } +bool SparseHistogram::SerializeInfoImpl(Pickle* pickle) const { + return pickle->WriteString(histogram_name()) && pickle->WriteInt(flags()); +} + SparseHistogram::SparseHistogram(const string& name) - : HistogramBase(name), - redundant_count_(0) {} + : HistogramBase(name) {} + +HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) { + string histogram_name; + int flags; + if (!iter->ReadString(&histogram_name) || !iter->ReadInt(&flags)) { + DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; + return NULL; + } + + DCHECK(flags & HistogramBase::kIPCSerializationSourceFlag); + flags &= ~HistogramBase::kIPCSerializationSourceFlag; + + return SparseHistogram::FactoryGet(histogram_name, flags); +} void SparseHistogram::GetParameters(DictionaryValue* params) const { // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.) diff --git a/base/metrics/sparse_histogram.h b/base/metrics/sparse_histogram.h index a7c5695..346ffeb 100644 --- a/base/metrics/sparse_histogram.h +++ b/base/metrics/sparse_histogram.h @@ -13,6 +13,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram_base.h" +#include "base/metrics/sample_map.h" #include "base/synchronization/lock.h" namespace base { @@ -33,24 +34,35 @@ class BASE_EXPORT_PRIVATE SparseHistogram : public HistogramBase { Sample maximum, size_t bucket_count) const OVERRIDE; virtual void Add(Sample value) OVERRIDE; + virtual void AddSamples(const HistogramSamples& samples) OVERRIDE; + virtual bool AddSamplesFromPickle(PickleIterator* iter) OVERRIDE; + virtual scoped_ptr<HistogramSamples> SnapshotSamples() const OVERRIDE; virtual void WriteHTMLGraph(std::string* output) const OVERRIDE; virtual void WriteAscii(std::string* output) const OVERRIDE; - virtual scoped_ptr<HistogramSamples> SnapshotSamples() const OVERRIDE; + + protected: + // HistogramBase implementation: + virtual bool SerializeInfoImpl(Pickle* pickle) const OVERRIDE; private: // Clients should always use FactoryGet to create SparseHistogram. SparseHistogram(const std::string& name); + friend BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo( + PickleIterator* iter); + static HistogramBase* DeserializeInfoImpl(PickleIterator* iter); + virtual void GetParameters(DictionaryValue* params) const OVERRIDE; virtual void GetCountAndBucketData(Count* count, ListValue* buckets) const OVERRIDE; - friend class SparseHistogramTest; // For constuctor calling. + // For constuctor calling. + friend class SparseHistogramTest; - // Protects access to |sample_counts_| and |redundant_count_|. + // Protects access to |samples_|. mutable base::Lock lock_; - std::map<HistogramBase::Sample, HistogramBase::Count> sample_counts_; - HistogramBase::Count redundant_count_; + + SampleMap samples_; DISALLOW_COPY_AND_ASSIGN(SparseHistogram); }; diff --git a/base/metrics/sparse_histogram_unittest.cc b/base/metrics/sparse_histogram_unittest.cc index b4eb8fd..1b52b6c 100644 --- a/base/metrics/sparse_histogram_unittest.cc +++ b/base/metrics/sparse_histogram_unittest.cc @@ -5,8 +5,10 @@ #include <string> #include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram_base.h" #include "base/metrics/sample_map.h" #include "base/metrics/sparse_histogram.h" +#include "base/pickle.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -19,7 +21,7 @@ class SparseHistogramTest : public testing::Test { }; TEST_F(SparseHistogramTest, BasicTest) { - scoped_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse1")); + scoped_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse")); scoped_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples()); EXPECT_EQ(0, snapshot->TotalCount()); EXPECT_EQ(0, snapshot->sum()); @@ -37,4 +39,29 @@ TEST_F(SparseHistogramTest, BasicTest) { EXPECT_EQ(1, snapshot2->GetCount(101)); } +TEST_F(SparseHistogramTest, Serialize) { + scoped_ptr<SparseHistogram> histogram(NewSparseHistogram("Sparse")); + histogram->SetFlags(HistogramBase::kIPCSerializationSourceFlag); + + Pickle pickle; + histogram->SerializeInfo(&pickle); + + PickleIterator iter(pickle); + + int type; + EXPECT_TRUE(iter.ReadInt(&type)); + EXPECT_EQ(SPARSE_HISTOGRAM, type); + + std::string name; + EXPECT_TRUE(iter.ReadString(&name)); + EXPECT_EQ("Sparse", name); + + int flag; + EXPECT_TRUE(iter.ReadInt(&flag)); + EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, flag); + + // No more data in the pickle. + EXPECT_FALSE(iter.SkipBytes(1)); +} + } // namespace base diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h index 5345735..6d40aff 100644 --- a/base/metrics/statistics_recorder.h +++ b/base/metrics/statistics_recorder.h @@ -90,6 +90,7 @@ class BASE_EXPORT StatisticsRecorder { typedef std::map<uint32, std::list<const BucketRanges*>*> RangesMap; friend struct DefaultLazyInstanceTraits<StatisticsRecorder>; + friend class HistogramBaseTest; friend class HistogramTest; friend class StatisticsRecorderTest; |