diff options
author | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-06 00:09:37 +0000 |
---|---|---|
committer | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-06 00:09:37 +0000 |
commit | e8829a1981a2d9d849c377c28f9444fdefee0f44 (patch) | |
tree | 3cfe522cf76d308dec9fca773d3f9495e12abc5e /base | |
parent | 4f3b65a30cad88c1f1e482f7bda69ef50f8e1364 (diff) | |
download | chromium_src-e8829a1981a2d9d849c377c28f9444fdefee0f44.zip chromium_src-e8829a1981a2d9d849c377c28f9444fdefee0f44.tar.gz chromium_src-e8829a1981a2d9d849c377c28f9444fdefee0f44.tar.bz2 |
Use factory to create histograms, and refcounts to track lifetimes
This is CL patch 377028 by Raman Tenneti, with minor changes to
make the try-bots happier.
It is cleanup that better ensures lifetimes of histograms (making it harder
for users to abuse them).
bug=16495 (repairs leak induced by the first landing)
bug=18840 (should make leaks less possible)
tbr=raman.tenneti
Review URL: http://codereview.chromium.org/462027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@33933 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/histogram.cc | 262 | ||||
-rw-r--r-- | base/histogram.h | 317 | ||||
-rw-r--r-- | base/histogram_unittest.cc | 129 | ||||
-rw-r--r-- | base/message_loop.cc | 7 | ||||
-rw-r--r-- | base/message_loop.h | 2 |
5 files changed, 485 insertions, 232 deletions
diff --git a/base/histogram.cc b/base/histogram.cc index 55af96d..3a5a5b2 100644 --- a/base/histogram.cc +++ b/base/histogram.cc @@ -23,7 +23,42 @@ typedef Histogram::Count Count; // static const int Histogram::kHexRangePrintingFlag = 0x8000; -Histogram::Histogram(const char* name, Sample minimum, +scoped_refptr<Histogram> Histogram::HistogramFactoryGet( + const std::string& name, Sample minimum, Sample maximum, + size_t bucket_count) { + scoped_refptr<Histogram> histogram(NULL); + + // Defensive code. + if (minimum <= 0) + minimum = 1; + if (maximum >= kSampleType_MAX) + maximum = kSampleType_MAX - 1; + + if (StatisticsRecorder::FindHistogram(name, &histogram)) { + DCHECK(histogram.get() != NULL); + } else { + histogram = new Histogram(name, minimum, maximum, bucket_count); + scoped_refptr<Histogram> registered_histogram(NULL); + StatisticsRecorder::FindHistogram(name, ®istered_histogram); + // Allow a NULL return to mean that the StatisticsRecorder was not started. + if (registered_histogram.get() != NULL && + registered_histogram.get() != histogram.get()) + histogram = registered_histogram; + } + + DCHECK(HISTOGRAM == histogram->histogram_type()); + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + return histogram; +} + +scoped_refptr<Histogram> Histogram::HistogramFactoryGet( + const std::string& name, base::TimeDelta minimum, base::TimeDelta maximum, + size_t bucket_count) { + return HistogramFactoryGet(name, + minimum.InMilliseconds(), maximum.InMilliseconds(), bucket_count); +} + +Histogram::Histogram(const std::string& name, Sample minimum, Sample maximum, size_t bucket_count) : histogram_name_(name), declared_min_(minimum), @@ -31,12 +66,11 @@ Histogram::Histogram(const char* name, Sample minimum, bucket_count_(bucket_count), flags_(0), ranges_(bucket_count + 1, 0), - sample_(), - registered_(false) { + sample_() { Initialize(); } -Histogram::Histogram(const char* name, TimeDelta minimum, +Histogram::Histogram(const std::string& name, TimeDelta minimum, TimeDelta maximum, size_t bucket_count) : histogram_name_(name), declared_min_(static_cast<int> (minimum.InMilliseconds())), @@ -44,21 +78,23 @@ Histogram::Histogram(const char* name, TimeDelta minimum, bucket_count_(bucket_count), flags_(0), ranges_(bucket_count + 1, 0), - sample_(), - registered_(false) { + sample_() { Initialize(); } Histogram::~Histogram() { - if (registered_) - StatisticsRecorder::UnRegister(this); + DCHECK(!(kPlannedLeakFlag & flags_)); + if (StatisticsRecorder::dump_on_exit()) { + std::string output; + WriteAscii(true, "\n", &output); + LOG(INFO) << output; + } + // Just to make sure most derived class did this properly... DCHECK(ValidateBucketRanges()); } void Histogram::Add(int value) { - if (!registered_) - registered_ = StatisticsRecorder::Register(this); if (value >= kSampleType_MAX) value = kSampleType_MAX - 1; if (value < 0) @@ -170,7 +206,7 @@ void Histogram::Initialize() { ranges_[bucket_count_] = kSampleType_MAX; InitializeBucketRange(); DCHECK(ValidateBucketRanges()); - registered_ = StatisticsRecorder::Register(this); + StatisticsRecorder::Register(this); } // Calculate what range of values are held in each bucket. @@ -353,14 +389,15 @@ void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, // static std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, const SampleSet& snapshot) { - Pickle pickle; + DCHECK(histogram.histogram_type() != NOT_VALID_IN_RENDERER); + Pickle pickle; pickle.WriteString(histogram.histogram_name()); pickle.WriteInt(histogram.declared_min()); pickle.WriteInt(histogram.declared_max()); pickle.WriteSize(histogram.bucket_count()); pickle.WriteInt(histogram.histogram_type()); - pickle.WriteInt(histogram.flags()); + pickle.WriteInt(histogram.flags() & ~kIPCSerializationSourceFlag); snapshot.Serialize(&pickle); return std::string(static_cast<const char*>(pickle.data()), pickle.size()); @@ -394,27 +431,27 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { return false; } - Histogram* render_histogram = - StatisticsRecorder::GetHistogram(histogram_name); - - if (render_histogram == NULL) { - if (histogram_type == EXPONENTIAL) { - render_histogram = new Histogram(histogram_name.c_str(), - declared_min, - declared_max, - bucket_count); - } else if (histogram_type == LINEAR) { - render_histogram = new LinearHistogram(histogram_name.c_str(), - declared_min, - declared_max, - bucket_count); - } else { - LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " << - histogram_type; - return false; - } - DCHECK(!(flags & kRendererHistogramFlag)); - render_histogram->SetFlags(flags | kRendererHistogramFlag); + DCHECK(histogram_type != NOT_VALID_IN_RENDERER); + + scoped_refptr<Histogram> render_histogram(NULL); + + if (histogram_type == HISTOGRAM) { + render_histogram = Histogram::HistogramFactoryGet( + histogram_name, declared_min, declared_max, bucket_count); + } else if (histogram_type == LINEAR_HISTOGRAM) { + render_histogram = LinearHistogram::LinearHistogramFactoryGet( + histogram_name, declared_min, declared_max, bucket_count); + } else if (histogram_type == BOOLEAN_HISTOGRAM) { + render_histogram = BooleanHistogram::BooleanHistogramFactoryGet( + histogram_name); + } else if (histogram_type == THREAD_SAFE_HISTOGRAM) { + render_histogram = + ThreadSafeHistogram::ThreadSafeHistogramFactoryGet( + histogram_name, declared_min, declared_max, bucket_count); + } else { + LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " << + histogram_type; + return false; } DCHECK(declared_min == render_histogram->declared_min()); @@ -422,17 +459,16 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { DCHECK(bucket_count == render_histogram->bucket_count()); DCHECK(histogram_type == render_histogram->histogram_type()); - if (render_histogram->flags() & kRendererHistogramFlag) { - render_histogram->AddSampleSet(sample); - } else { - DLOG(INFO) << "Single thread mode, histogram observed and not copied: " << + if (render_histogram->flags() & kIPCSerializationSourceFlag) { + DLOG(INFO) << "Single process mode, histogram observed and not copied: " << histogram_name; + } else { + render_histogram->AddSampleSet(sample); } return true; } - //------------------------------------------------------------------------------ // Methods for the Histogram::SampleSet class //------------------------------------------------------------------------------ @@ -537,14 +573,48 @@ bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { // buckets. //------------------------------------------------------------------------------ -LinearHistogram::LinearHistogram(const char* name, Sample minimum, +scoped_refptr<Histogram> LinearHistogram::LinearHistogramFactoryGet( + const std::string& name, Sample minimum, Sample maximum, + size_t bucket_count) { + scoped_refptr<Histogram> histogram(NULL); + + if (minimum <= 0) + minimum = 1; + if (maximum >= kSampleType_MAX) + maximum = kSampleType_MAX - 1; + + if (StatisticsRecorder::FindHistogram(name, &histogram)) { + DCHECK(histogram.get() != NULL); + } else { + histogram = new LinearHistogram(name, minimum, maximum, bucket_count); + scoped_refptr<Histogram> registered_histogram(NULL); + StatisticsRecorder::FindHistogram(name, ®istered_histogram); + if (registered_histogram.get() != NULL && + registered_histogram.get() != histogram.get()) + histogram = registered_histogram; + } + + DCHECK(LINEAR_HISTOGRAM == histogram->histogram_type()); + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + + return histogram; +} + +scoped_refptr<Histogram> LinearHistogram::LinearHistogramFactoryGet( + const std::string& name, base::TimeDelta minimum, base::TimeDelta maximum, + size_t bucket_count) { + return LinearHistogramFactoryGet(name, minimum.InMilliseconds(), + maximum.InMilliseconds(), bucket_count); +} + +LinearHistogram::LinearHistogram(const std::string& name, Sample minimum, Sample maximum, size_t bucket_count) : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { InitializeBucketRange(); DCHECK(ValidateBucketRanges()); } -LinearHistogram::LinearHistogram(const char* name, +LinearHistogram::LinearHistogram(const std::string& name, TimeDelta minimum, TimeDelta maximum, size_t bucket_count) : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? minimum : TimeDelta::FromMilliseconds(1), @@ -595,11 +665,61 @@ double LinearHistogram::GetBucketSize(Count current, size_t i) const { } //------------------------------------------------------------------------------ +// This section provides implementation for BooleanHistogram. +//------------------------------------------------------------------------------ + +scoped_refptr<Histogram> BooleanHistogram::BooleanHistogramFactoryGet( + const std::string& name) { + scoped_refptr<Histogram> histogram(NULL); + + if (StatisticsRecorder::FindHistogram(name, &histogram)) { + DCHECK(histogram.get() != NULL); + } else { + histogram = new BooleanHistogram(name); + scoped_refptr<Histogram> registered_histogram(NULL); + StatisticsRecorder::FindHistogram(name, ®istered_histogram); + if (registered_histogram.get() != NULL && + registered_histogram.get() != histogram.get()) + histogram = registered_histogram; + } + + DCHECK(BOOLEAN_HISTOGRAM == histogram->histogram_type()); + + return histogram; +} + +//------------------------------------------------------------------------------ // This section provides implementation for ThreadSafeHistogram. //------------------------------------------------------------------------------ -ThreadSafeHistogram::ThreadSafeHistogram(const char* name, Sample minimum, - Sample maximum, size_t bucket_count) +scoped_refptr<Histogram> ThreadSafeHistogram::ThreadSafeHistogramFactoryGet( + const std::string& name, Sample minimum, Sample maximum, + size_t bucket_count) { + scoped_refptr<Histogram> histogram(NULL); + + if (minimum <= 0) + minimum = 1; + if (maximum >= kSampleType_MAX) + maximum = kSampleType_MAX - 1; + + if (StatisticsRecorder::FindHistogram(name, &histogram)) { + DCHECK(histogram.get() != NULL); + } else { + histogram = new ThreadSafeHistogram(name, minimum, maximum, bucket_count); + scoped_refptr<Histogram> registered_histogram(NULL); + StatisticsRecorder::FindHistogram(name, ®istered_histogram); + if (registered_histogram.get() != NULL && + registered_histogram.get() != histogram.get()) + histogram = registered_histogram; + } + + DCHECK(THREAD_SAFE_HISTOGRAM == histogram->histogram_type()); + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + return histogram; +} + +ThreadSafeHistogram::ThreadSafeHistogram(const std::string& name, + Sample minimum, Sample maximum, size_t bucket_count) : Histogram(name, minimum, maximum, bucket_count), lock_() { } @@ -657,34 +777,22 @@ bool StatisticsRecorder::WasStarted() { return NULL != histograms_; } +// Note: We can't accept a ref_ptr to |histogram| because we *might* not keep a +// reference, and we are called while in the Histogram constructor. In that +// scenario, a ref_ptr would have incremented the ref count when the histogram +// was passed to us, decremented it when we returned, and the instance would be +// destroyed before assignment (when value was returned by new). // static -bool StatisticsRecorder::Register(Histogram* histogram) { +void StatisticsRecorder::Register(Histogram* histogram) { if (!histograms_) - return false; + return; const std::string name = histogram->histogram_name(); AutoLock auto_lock(*lock_); - if (histograms_->end() != histograms_->find(name)) { - // Check to be sure it is compatible.... and if not, then do a CHECK() - return false; // This name is already registered. - } - (*histograms_)[name] = histogram; - return true; -} + DCHECK(histograms_->end() == histograms_->find(name)); -// static -void StatisticsRecorder::UnRegister(Histogram* histogram) { - if (!histograms_) - return; - const std::string name = histogram->histogram_name(); - AutoLock auto_lock(*lock_); - DCHECK(histograms_->end() != histograms_->find(name)); - histograms_->erase(name); - if (dump_on_exit_) { - std::string output; - histogram->WriteAscii(true, "\n", &output); - LOG(INFO) << output; - } + (*histograms_)[name] = histogram; + return; } // static @@ -743,17 +851,31 @@ void StatisticsRecorder::GetHistograms(Histograms* output) { } } -Histogram* StatisticsRecorder::GetHistogram(const std::string& query) { +// static +void StatisticsRecorder::GetHistogramsForRenderer(Histograms* output) { if (!histograms_) - return NULL; + return; AutoLock auto_lock(*lock_); for (HistogramMap::iterator it = histograms_->begin(); histograms_->end() != it; ++it) { - if (it->first.find(query) != std::string::npos) - return it->second; + scoped_refptr<Histogram> histogram = it->second; + if (!(histogram->flags() & kIPCSerializationSourceFlag)) + histogram->SetFlags(kIPCSerializationSourceFlag); + output->push_back(histogram); } - return NULL; +} + +bool StatisticsRecorder::FindHistogram(const std::string& name, + scoped_refptr<Histogram>* histogram) { + if (!histograms_) + return false; + AutoLock auto_lock(*lock_); + HistogramMap::iterator it = histograms_->find(name); + if (histograms_->end() == it) + return false; + *histogram = it->second; + return true; } // private static diff --git a/base/histogram.h b/base/histogram.h index 4d40c1b..0c94fc0 100644 --- a/base/histogram.h +++ b/base/histogram.h @@ -36,6 +36,8 @@ #include <vector> #include "base/lock.h" +#include "base/ref_counted.h" +#include "base/logging.h" #include "base/time.h" //------------------------------------------------------------------------------ @@ -43,47 +45,56 @@ // The first four macros use 50 buckets. #define HISTOGRAM_TIMES(name, sample) do { \ - static Histogram counter((name), base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromSeconds(10), 50); \ - counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50); \ + counter->AddTime(sample); \ } while (0) #define HISTOGRAM_COUNTS(name, sample) do { \ - static Histogram counter((name), 1, 1000000, 50); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1, 1000000, 50); \ + counter->Add(sample); \ } while (0) #define HISTOGRAM_COUNTS_100(name, sample) do { \ - static Histogram counter((name), 1, 100, 50); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1, 100, 50); \ + counter->Add(sample); \ } while (0) #define HISTOGRAM_COUNTS_10000(name, sample) do { \ - static Histogram counter((name), 1, 10000, 50); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1, 10000, 50); \ + counter->Add(sample); \ } while (0) #define HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \ - static Histogram counter((name), min, max, bucket_count); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), min, max, bucket_count); \ + counter->Add(sample); \ } while (0) #define HISTOGRAM_PERCENTAGE(name, under_one_hundred) do { \ - static LinearHistogram counter((name), 1, 100, 101); \ - counter.Add(under_one_hundred); \ + static scoped_refptr<Histogram> counter = \ + LinearHistogram::LinearHistogramFactoryGet(\ + (name), 1, 100, 101); \ + counter->Add(under_one_hundred); \ } while (0) // For folks that need real specific times, use this to select a precise range // of times you want plotted, and the number of buckets you want used. #define HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \ - static Histogram counter((name), min, max, bucket_count); \ - counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), min, max, bucket_count); \ + counter->AddTime(sample); \ } while (0) // DO NOT USE THIS. It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES. #define HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ - static Histogram counter((name), min, max, bucket_count); \ - if ((sample) < (max)) counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), min, max, bucket_count); \ + if ((sample) < (max)) counter->AddTime(sample); \ } while (0) //------------------------------------------------------------------------------ @@ -101,12 +112,14 @@ // be equal in number or fewer than the corresponding calls to Add(). #define ASSET_HISTOGRAM_COUNTS(name, sample) do { \ - static ThreadSafeHistogram counter((name), 1, 1000000, 50); \ + static scoped_refptr<Histogram> counter = \ + ThreadSafeHistogram::ThreadSafeHistogramFactoryGet(\ + (name), 1, 1000000, 50); \ if (0 == sample) break; \ if (sample >= 0) \ - counter.Add(sample); \ + counter->Add(sample); \ else\ - counter.Remove(-sample); \ + counter->Remove(-sample); \ } while (0) //------------------------------------------------------------------------------ @@ -150,92 +163,122 @@ static const int kUmaTargetedHistogramFlag = 0x1; -// This indicates the histogram is shadow copy of renderer histrogram -// constructed by unpick method and updated regularly from renderer upload -// of histograms. -static const int kRendererHistogramFlag = 1 << 4; +// This indicates the histogram is pickled to be sent across an IPC Channel. +// If we observe this flag during unpickle method, then we are running in a +// single process mode. +static const int kIPCSerializationSourceFlag = 1 << 4; + +// Some histograms aren't currently destroyed. Until such users properly +// decref those histograms, we will mark there histograms as planned to leak so +// that we can catch any user that directly tries to call delete "directly" +// rather than using the reference counting features that should take care of +// this. +// TODO(jar): Make this flag unnecessary! +static const int kPlannedLeakFlag = 1 << 5; #define UMA_HISTOGRAM_TIMES(name, sample) do { \ - static Histogram counter((name), base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromSeconds(10), 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->AddTime(sample); \ } while (0) #define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) do { \ - static Histogram counter((name), base::TimeDelta::FromMilliseconds(10), \ - base::TimeDelta::FromMinutes(3), 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), base::TimeDelta::FromMilliseconds(10), \ + base::TimeDelta::FromMinutes(3), 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->AddTime(sample); \ } while (0) // Use this macro when times can routinely be much longer than 10 seconds. #define UMA_HISTOGRAM_LONG_TIMES(name, sample) do { \ - static Histogram counter((name), base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromHours(1), 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromHours(1), 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->AddTime(sample); \ } while (0) #define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \ - static Histogram counter((name), min, max, bucket_count); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), min, max, bucket_count); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->AddTime(sample); \ } while (0) #define UMA_HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ - static Histogram counter((name), min, max, bucket_count); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - if ((sample) < (max)) counter.AddTime(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), min, max, bucket_count); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + if ((sample) < (max)) counter->AddTime(sample); \ } while (0) #define UMA_HISTOGRAM_COUNTS(name, sample) do { \ - static Histogram counter((name), 1, 1000000, 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1, 1000000, 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->Add(sample); \ } while (0) #define UMA_HISTOGRAM_COUNTS_100(name, sample) do { \ - static Histogram counter((name), 1, 100, 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1, 100, 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->Add(sample); \ } while (0) #define UMA_HISTOGRAM_COUNTS_10000(name, sample) do { \ - static Histogram counter((name), 1, 10000, 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1, 10000, 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->Add(sample); \ } while (0) #define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \ - static Histogram counter((name), min, max, bucket_count); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), min, max, bucket_count); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->Add(sample); \ } while (0) #define UMA_HISTOGRAM_MEMORY_KB(name, sample) do { \ - static Histogram counter((name), 1000, 500000, 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1000, 500000, 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->Add(sample); \ } while (0) #define UMA_HISTOGRAM_MEMORY_MB(name, sample) do { \ - static Histogram counter((name), 1, 1000, 50); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.Add(sample); \ + static scoped_refptr<Histogram> counter = Histogram::HistogramFactoryGet(\ + (name), 1, 1000, 50); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->Add(sample); \ } while (0) #define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) do { \ - static LinearHistogram counter((name), 1, 100, 101); \ - counter.SetFlags(kUmaTargetedHistogramFlag); \ - counter.Add(under_one_hundred); \ + static scoped_refptr<Histogram> counter = \ + LinearHistogram::LinearHistogramFactoryGet(\ + (name), 1, 100, 101); \ + counter->SetFlags(kUmaTargetedHistogramFlag); \ + counter->Add(under_one_hundred); \ } while (0) //------------------------------------------------------------------------------ class Pickle; +class Histogram; +class LinearHistogram; +class BooleanHistogram; +class ThreadSafeHistogram; + +namespace disk_cache { + class StatsHistogram; +}; // namespace disk_cache -class Histogram { + +class Histogram : public base::RefCountedThreadSafe<Histogram> { public: typedef int Sample; // Used for samples (and ranges of samples). typedef int Count; // Used to count samples in a bucket. @@ -246,11 +289,26 @@ class Histogram { static const int kHexRangePrintingFlag; + /* These enums are meant to facilitate deserialization of renderer histograms + into the browser. */ + enum ClassType { + HISTOGRAM, + LINEAR_HISTOGRAM, + BOOLEAN_HISTOGRAM, + THREAD_SAFE_HISTOGRAM, + NOT_VALID_IN_RENDERER + }; + enum BucketLayout { EXPONENTIAL, LINEAR }; + struct DescriptionPair { + Sample sample; + const char* description; // Null means end of a list of pairs. + }; + //---------------------------------------------------------------------------- // Statistic values, developed over the life of the histogram. @@ -288,14 +346,18 @@ class Histogram { int64 square_sum_; // sum of squares of samples. }; //---------------------------------------------------------------------------- - - Histogram(const char* name, Sample minimum, - Sample maximum, size_t bucket_count); - Histogram(const char* name, base::TimeDelta minimum, - base::TimeDelta maximum, size_t bucket_count); - virtual ~Histogram(); + // minimum should start from 1. 0 is invalid as a minimum. 0 is an implicit + // default underflow bucket. + static scoped_refptr<Histogram> HistogramFactoryGet(const std::string& name, + Sample minimum, Sample maximum, size_t bucket_count); + static scoped_refptr<Histogram> HistogramFactoryGet(const std::string& name, + base::TimeDelta minimum, base::TimeDelta maximum, size_t bucket_count); void Add(int value); + + // This method is an interface, used only by BooleanHistogram. + virtual void AddBoolean(bool value) { DCHECK(false); } + // Accept a TimeDelta to increment. void AddTime(base::TimeDelta time) { Add(static_cast<int>(time.InMilliseconds())); @@ -303,6 +365,13 @@ class Histogram { void AddSampleSet(const SampleSet& sample); + // This method is an interface, used only by ThreadSafeHistogram. + virtual void Remove(int value) { DCHECK(false); } + + // This method is an interface, used only by LinearHistogram. + virtual void SetRangeDescriptions(const DescriptionPair descriptions[]) + { DCHECK(false); } + // The following methods provide graphical histogram displays. void WriteHTMLGraph(std::string* output) const; void WriteAscii(bool graph_it, const std::string& newline, @@ -315,8 +384,6 @@ class Histogram { void ClearFlags(int flags) { flags_ &= ~flags; } int flags() const { return flags_; } - virtual BucketLayout histogram_type() const { return EXPONENTIAL; } - // 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 @@ -332,10 +399,10 @@ class Histogram { // browser process. static bool DeserializeHistogramInfo(const std::string& histogram_info); - //---------------------------------------------------------------------------- - // Accessors for serialization and testing. + // Accessors for factory constuction, serialization and testing. //---------------------------------------------------------------------------- + virtual ClassType histogram_type() const { return HISTOGRAM; } const std::string histogram_name() const { return histogram_name_; } Sample declared_min() const { return declared_min_; } Sample declared_max() const { return declared_max_; } @@ -345,7 +412,28 @@ class Histogram { // Override with atomic/locked snapshot if needed. virtual void SnapshotSample(SampleSet* sample) const; + virtual bool HasConstructorArguments(Sample minimum, Sample maximum, + size_t bucket_count) { + return ((minimum == declared_min_) && (maximum == declared_max_) && + (bucket_count == bucket_count_)); + } + + virtual bool HasConstructorTimeDeltaArguments(base::TimeDelta minimum, + base::TimeDelta maximum, size_t bucket_count) { + return ((minimum.InMilliseconds() == declared_min_) && + (maximum.InMilliseconds() == declared_max_) && + (bucket_count == bucket_count_)); + } + protected: + friend class base::RefCountedThreadSafe<Histogram>; + Histogram(const std::string& name, Sample minimum, + Sample maximum, size_t bucket_count); + Histogram(const std::string& name, base::TimeDelta minimum, + base::TimeDelta maximum, size_t bucket_count); + + virtual ~Histogram(); + // Method to override to skip the display of the i'th bucket if it's empty. virtual bool PrintEmptyBucket(size_t index) const { return true; } @@ -434,9 +522,6 @@ class Histogram { // sample. SampleSet sample_; - // Indicate if successfully registered. - bool registered_; - DISALLOW_COPY_AND_ASSIGN(Histogram); }; @@ -446,24 +531,30 @@ class Histogram { // buckets. class LinearHistogram : public Histogram { public: - struct DescriptionPair { - Sample sample; - const char* description; // Null means end of a list of pairs. - }; - LinearHistogram(const char* name, Sample minimum, - Sample maximum, size_t bucket_count); - - LinearHistogram(const char* name, base::TimeDelta minimum, - base::TimeDelta maximum, size_t bucket_count); - ~LinearHistogram() {} + virtual ClassType histogram_type() const { return LINEAR_HISTOGRAM; } // Store a list of number/text values for use in rendering the histogram. // The last element in the array has a null in its "description" slot. - void SetRangeDescriptions(const DescriptionPair descriptions[]); + virtual void SetRangeDescriptions(const DescriptionPair descriptions[]); - virtual BucketLayout histogram_type() const { return LINEAR; } + /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit + default underflow bucket. */ + static scoped_refptr<Histogram> LinearHistogramFactoryGet( + const std::string& name, Sample minimum, Sample maximum, + size_t bucket_count); + static scoped_refptr<Histogram> LinearHistogramFactoryGet( + const std::string& name, base::TimeDelta minimum, + base::TimeDelta maximum, size_t bucket_count); protected: + LinearHistogram(const std::string& name, Sample minimum, + Sample maximum, size_t bucket_count); + + LinearHistogram(const std::string& name, base::TimeDelta minimum, + base::TimeDelta maximum, size_t bucket_count); + + virtual ~LinearHistogram() {} + // Initialize ranges_ mapping. virtual void InitializeBucketRange(); virtual double GetBucketSize(Count current, size_t i) const; @@ -491,13 +582,18 @@ class LinearHistogram : public Histogram { // BooleanHistogram is a histogram for booleans. class BooleanHistogram : public LinearHistogram { public: - explicit BooleanHistogram(const char* name) - : LinearHistogram(name, 0, 2, 3) { - } + static scoped_refptr<Histogram> BooleanHistogramFactoryGet( + const std::string& name); + + virtual ClassType histogram_type() const { return BOOLEAN_HISTOGRAM; } - void AddBoolean(bool value) { Add(value ? 1 : 0); } + virtual void AddBoolean(bool value) { Add(value ? 1 : 0); } private: + explicit BooleanHistogram(const std::string& name) + : LinearHistogram(name, 1, 2, 3) { + } + DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); }; @@ -507,13 +603,21 @@ class BooleanHistogram : public LinearHistogram { class ThreadSafeHistogram : public Histogram { public: - ThreadSafeHistogram(const char* name, Sample minimum, - Sample maximum, size_t bucket_count); + static scoped_refptr<Histogram> ThreadSafeHistogramFactoryGet( + const std::string& name, Sample minimum, Sample maximum, + size_t bucket_count); + + virtual ClassType histogram_type() const { return THREAD_SAFE_HISTOGRAM; } // Provide the analog to Add() - void Remove(int value); + virtual void Remove(int value); protected: + ThreadSafeHistogram(const std::string& name, Sample minimum, + Sample maximum, size_t bucket_count); + + virtual ~ThreadSafeHistogram() {} + // Provide locked versions to get precise counts. virtual void Accumulate(Sample value, Count count, size_t index); @@ -532,7 +636,7 @@ class ThreadSafeHistogram : public Histogram { class StatisticsRecorder { public: - typedef std::vector<Histogram*> Histograms; + typedef std::vector<scoped_refptr<Histogram> > Histograms; StatisticsRecorder(); @@ -542,10 +646,7 @@ class StatisticsRecorder { static bool WasStarted(); // Register, or add a new histogram to the collection of statistics. - // Return true if registered. - static bool Register(Histogram* histogram); - // Unregister, or remove, a histogram from the collection of statistics. - static void UnRegister(Histogram* histogram); + static void Register(Histogram* histogram); // Methods for printing histograms. Only histograms which have query as // a substring are written to output (an empty string will process all @@ -556,8 +657,16 @@ class StatisticsRecorder { // Method for extracting histograms which were marked for use by UMA. static void GetHistograms(Histograms* output); - // Find a histogram by name. This method is thread safe. - static Histogram* GetHistogram(const std::string& query); + // Method for extracting histograms for renderer and the histogram's flag is + // set to kIPCSerializationSourceFlag. + static void GetHistogramsForRenderer(Histograms* output); + + // Find a histogram by name. It matches the exact name. This method is thread + // safe. + static bool FindHistogram(const std::string& query, + scoped_refptr<Histogram>* histogram); + + static bool dump_on_exit() { return dump_on_exit_; } static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; } @@ -570,7 +679,7 @@ class StatisticsRecorder { private: // We keep all registered histograms in a map, from name to histogram. - typedef std::map<std::string, Histogram*> HistogramMap; + typedef std::map<std::string, scoped_refptr<Histogram> > HistogramMap; static HistogramMap* histograms_; diff --git a/base/histogram_unittest.cc b/base/histogram_unittest.cc index 3f2ed4c..4d5de51 100644 --- a/base/histogram_unittest.cc +++ b/base/histogram_unittest.cc @@ -19,11 +19,17 @@ class HistogramTest : public testing::Test { // Check for basic syntax and use. TEST(HistogramTest, StartupShutdownTest) { // Try basic construction - Histogram histogram("TestHistogram", 1, 1000, 10); - Histogram histogram1("Test1Histogram", 1, 1000, 10); - - LinearHistogram linear_histogram("TestLinearHistogram", 1, 1000, 10); - LinearHistogram linear_histogram1("Test1LinearHistogram", 1, 1000, 10); + scoped_refptr<Histogram> histogram = + Histogram::HistogramFactoryGet("TestHistogram", 1, 1000, 10); + scoped_refptr<Histogram> histogram1 = + Histogram::HistogramFactoryGet("Test1Histogram", 1, 1000, 10); + + scoped_refptr<Histogram> linear_histogram = + LinearHistogram::LinearHistogramFactoryGet("TestLinearHistogram", 1, 1000, + 10); + scoped_refptr<Histogram> linear_histogram1 = + LinearHistogram::LinearHistogramFactoryGet("Test1LinearHistogram", 1, + 1000, 10); // Use standard macros (but with fixed samples) HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1)); @@ -57,17 +63,23 @@ TEST(HistogramTest, RecordedStartupTest) { EXPECT_EQ(0U, histograms.size()); // Try basic construction - Histogram histogram("TestHistogram", 1, 1000, 10); + scoped_refptr<Histogram> histogram = + Histogram::HistogramFactoryGet("TestHistogram", 1, 1000, 10); histograms.clear(); StatisticsRecorder::GetHistograms(&histograms); // Load up lists EXPECT_EQ(1U, histograms.size()); - Histogram histogram1("Test1Histogram", 1, 1000, 10); + scoped_refptr<Histogram> histogram1 = + Histogram::HistogramFactoryGet("Test1Histogram", 1, 1000, 10); histograms.clear(); StatisticsRecorder::GetHistograms(&histograms); // Load up lists EXPECT_EQ(2U, histograms.size()); - LinearHistogram linear_histogram("TestLinearHistogram", 1, 1000, 10); - LinearHistogram linear_histogram1("Test1LinearHistogram", 1, 1000, 10); + scoped_refptr<Histogram> linear_histogram = + LinearHistogram::LinearHistogramFactoryGet( + "TestLinearHistogram", 1, 1000, 10); + scoped_refptr<Histogram> linear_histogram1 = + LinearHistogram::LinearHistogramFactoryGet( + "Test1LinearHistogram", 1, 1000, 10); histograms.clear(); StatisticsRecorder::GetHistograms(&histograms); // Load up lists EXPECT_EQ(4U, histograms.size()); @@ -102,55 +114,62 @@ TEST(HistogramTest, RangeTest) { recorder.GetHistograms(&histograms); EXPECT_EQ(0U, histograms.size()); - Histogram histogram("Histogram", 1, 64, 8); // As mentioned in header file. + scoped_refptr<Histogram> histogram = Histogram::HistogramFactoryGet( + "Histogram", 1, 64, 8); // As mentioned in header file. // Check that we got a nice exponential when there was enough rooom. - EXPECT_EQ(0, histogram.ranges(0)); + EXPECT_EQ(0, histogram->ranges(0)); int power_of_2 = 1; for (int i = 1; i < 8; i++) { - EXPECT_EQ(power_of_2, histogram.ranges(i)); + EXPECT_EQ(power_of_2, histogram->ranges(i)); power_of_2 *= 2; } - EXPECT_EQ(INT_MAX, histogram.ranges(8)); + EXPECT_EQ(INT_MAX, histogram->ranges(8)); - Histogram short_histogram("Histogram Shortened", 1, 7, 8); + scoped_refptr<Histogram> short_histogram = + Histogram::HistogramFactoryGet("Histogram Shortened", 1, 7, 8); // Check that when the number of buckets is short, we get a linear histogram // for lack of space to do otherwise. for (int i = 0; i < 8; i++) - EXPECT_EQ(i, short_histogram.ranges(i)); - EXPECT_EQ(INT_MAX, short_histogram.ranges(8)); + EXPECT_EQ(i, short_histogram->ranges(i)); + EXPECT_EQ(INT_MAX, short_histogram->ranges(8)); - LinearHistogram linear_histogram("Linear", 1, 7, 8); + scoped_refptr<Histogram> linear_histogram = + LinearHistogram::LinearHistogramFactoryGet("Linear", 1, 7, 8); // We also get a nice linear set of bucket ranges when we ask for it for (int i = 0; i < 8; i++) - EXPECT_EQ(i, linear_histogram.ranges(i)); - EXPECT_EQ(INT_MAX, linear_histogram.ranges(8)); + EXPECT_EQ(i, linear_histogram->ranges(i)); + EXPECT_EQ(INT_MAX, linear_histogram->ranges(8)); - LinearHistogram linear_broad_histogram("Linear widened", 2, 14, 8); + scoped_refptr<Histogram> linear_broad_histogram = + LinearHistogram::LinearHistogramFactoryGet( + "Linear widened", 2, 14, 8); // ...but when the list has more space, then the ranges naturally spread out. for (int i = 0; i < 8; i++) - EXPECT_EQ(2 * i, linear_broad_histogram.ranges(i)); - EXPECT_EQ(INT_MAX, linear_broad_histogram.ranges(8)); + EXPECT_EQ(2 * i, linear_broad_histogram->ranges(i)); + EXPECT_EQ(INT_MAX, linear_broad_histogram->ranges(8)); - ThreadSafeHistogram threadsafe_histogram("ThreadSafe", 1, 32, 15); + scoped_refptr<Histogram> threadsafe_histogram = + ThreadSafeHistogram::ThreadSafeHistogramFactoryGet("ThreadSafe", 1, 32, + 15); // When space is a little tight, we transition from linear to exponential. // This is what happens in both the basic histogram, and the threadsafe // variant (which is derived). - EXPECT_EQ(0, threadsafe_histogram.ranges(0)); - EXPECT_EQ(1, threadsafe_histogram.ranges(1)); - EXPECT_EQ(2, threadsafe_histogram.ranges(2)); - EXPECT_EQ(3, threadsafe_histogram.ranges(3)); - EXPECT_EQ(4, threadsafe_histogram.ranges(4)); - EXPECT_EQ(5, threadsafe_histogram.ranges(5)); - EXPECT_EQ(6, threadsafe_histogram.ranges(6)); - EXPECT_EQ(7, threadsafe_histogram.ranges(7)); - EXPECT_EQ(9, threadsafe_histogram.ranges(8)); - EXPECT_EQ(11, threadsafe_histogram.ranges(9)); - EXPECT_EQ(14, threadsafe_histogram.ranges(10)); - EXPECT_EQ(17, threadsafe_histogram.ranges(11)); - EXPECT_EQ(21, threadsafe_histogram.ranges(12)); - EXPECT_EQ(26, threadsafe_histogram.ranges(13)); - EXPECT_EQ(32, threadsafe_histogram.ranges(14)); - EXPECT_EQ(INT_MAX, threadsafe_histogram.ranges(15)); + EXPECT_EQ(0, threadsafe_histogram->ranges(0)); + EXPECT_EQ(1, threadsafe_histogram->ranges(1)); + EXPECT_EQ(2, threadsafe_histogram->ranges(2)); + EXPECT_EQ(3, threadsafe_histogram->ranges(3)); + EXPECT_EQ(4, threadsafe_histogram->ranges(4)); + EXPECT_EQ(5, threadsafe_histogram->ranges(5)); + EXPECT_EQ(6, threadsafe_histogram->ranges(6)); + EXPECT_EQ(7, threadsafe_histogram->ranges(7)); + EXPECT_EQ(9, threadsafe_histogram->ranges(8)); + EXPECT_EQ(11, threadsafe_histogram->ranges(9)); + EXPECT_EQ(14, threadsafe_histogram->ranges(10)); + EXPECT_EQ(17, threadsafe_histogram->ranges(11)); + EXPECT_EQ(21, threadsafe_histogram->ranges(12)); + EXPECT_EQ(26, threadsafe_histogram->ranges(13)); + EXPECT_EQ(32, threadsafe_histogram->ranges(14)); + EXPECT_EQ(INT_MAX, threadsafe_histogram->ranges(15)); recorder.GetHistograms(&histograms); EXPECT_EQ(5U, histograms.size()); @@ -159,21 +178,22 @@ TEST(HistogramTest, RangeTest) { // Make sure histogram handles out-of-bounds data gracefully. TEST(HistogramTest, BoundsTest) { const size_t kBucketCount = 50; - Histogram histogram("Bounded", 10, 100, kBucketCount); + scoped_refptr<Histogram> histogram = Histogram::HistogramFactoryGet("Bounded", + 10, 100, kBucketCount); // Put two samples "out of bounds" above and below. - histogram.Add(5); - histogram.Add(-50); + histogram->Add(5); + histogram->Add(-50); - histogram.Add(100); - histogram.Add(10000); + histogram->Add(100); + histogram->Add(10000); // Verify they landed in the underflow, and overflow buckets. Histogram::SampleSet sample; - histogram.SnapshotSample(&sample); + histogram->SnapshotSample(&sample); EXPECT_EQ(2, sample.counts(0)); EXPECT_EQ(0, sample.counts(1)); - size_t array_size = histogram.bucket_count(); + size_t array_size = histogram->bucket_count(); EXPECT_EQ(kBucketCount, array_size); EXPECT_EQ(0, sample.counts(array_size - 2)); EXPECT_EQ(2, sample.counts(array_size - 1)); @@ -181,31 +201,32 @@ TEST(HistogramTest, BoundsTest) { // Check to be sure samples land as expected is "correct" buckets. TEST(HistogramTest, BucketPlacementTest) { - Histogram histogram("Histogram", 1, 64, 8); // As mentioned in header file. + scoped_refptr<Histogram> histogram = Histogram::HistogramFactoryGet( + "Histogram", 1, 64, 8); // As mentioned in header file. // Check that we got a nice exponential since there was enough rooom. - EXPECT_EQ(0, histogram.ranges(0)); + EXPECT_EQ(0, histogram->ranges(0)); int power_of_2 = 1; for (int i = 1; i < 8; i++) { - EXPECT_EQ(power_of_2, histogram.ranges(i)); + EXPECT_EQ(power_of_2, histogram->ranges(i)); power_of_2 *= 2; } - EXPECT_EQ(INT_MAX, histogram.ranges(8)); + EXPECT_EQ(INT_MAX, histogram->ranges(8)); // Add i+1 samples to the i'th bucket. - histogram.Add(0); + histogram->Add(0); power_of_2 = 1; for (int i = 1; i < 8; i++) { for (int j = 0; j <= i; j++) - histogram.Add(power_of_2); + histogram->Add(power_of_2); power_of_2 *= 2; } // Leave overflow bucket empty. // Check to see that the bucket counts reflect our additions. Histogram::SampleSet sample; - histogram.SnapshotSample(&sample); - EXPECT_EQ(INT_MAX, histogram.ranges(8)); + histogram->SnapshotSample(&sample); + EXPECT_EQ(INT_MAX, histogram->ranges(8)); for (int i = 0; i < 8; i++) EXPECT_EQ(i + 1, sample.counts(i)); } diff --git a/base/message_loop.cc b/base/message_loop.cc index ddca7dbf..64913e5 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -531,11 +531,12 @@ void MessageLoop::StartHistogrammer() { if (enable_histogrammer_ && !message_histogram_.get() && StatisticsRecorder::WasStarted()) { DCHECK(!thread_name_.empty()); - message_histogram_.reset( - new LinearHistogram(("MsgLoop:" + thread_name_).c_str(), + message_histogram_ = + LinearHistogram::LinearHistogramFactoryGet( + ("MsgLoop:" + thread_name_), kLeastNonZeroMessageId, kMaxMessageId, - kNumberOfDistinctMessagesDisplayed)); + kNumberOfDistinctMessagesDisplayed); message_histogram_->SetFlags(message_histogram_->kHexRangePrintingFlag); message_histogram_->SetRangeDescriptions(event_descriptions_); } diff --git a/base/message_loop.h b/base/message_loop.h index ff4531c..e45adde 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -397,7 +397,7 @@ class MessageLoop : public base::MessagePump::Delegate { std::string thread_name_; // A profiling histogram showing the counts of various messages and events. - scoped_ptr<LinearHistogram> message_histogram_; + scoped_refptr<Histogram> message_histogram_; // A null terminated list which creates an incoming_queue of tasks that are // aquired under a mutex for processing on this instance's thread. These tasks |