| // 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 "base/metrics/histogram_base.h" | |
| #include <limits.h> | |
| #include <memory> | |
| #include <set> | |
| #include <utility> | |
| #include "base/json/json_string_value_serializer.h" | |
| #include "base/lazy_instance.h" | |
| #include "base/logging.h" | |
| #include "base/metrics/histogram.h" | |
| #include "base/metrics/histogram_macros.h" | |
| #include "base/metrics/histogram_samples.h" | |
| #include "base/metrics/sparse_histogram.h" | |
| #include "base/metrics/statistics_recorder.h" | |
| #include "base/numerics/safe_conversions.h" | |
| #include "base/pickle.h" | |
| #include "base/process/process_handle.h" | |
| #include "base/rand_util.h" | |
| #include "base/strings/stringprintf.h" | |
| #include "base/synchronization/lock.h" | |
| #include "base/values.h" | |
| #include "starboard/types.h" | |
| namespace base { | |
| std::string HistogramTypeToString(HistogramType type) { | |
| switch (type) { | |
| case HISTOGRAM: | |
| return "HISTOGRAM"; | |
| case LINEAR_HISTOGRAM: | |
| return "LINEAR_HISTOGRAM"; | |
| case BOOLEAN_HISTOGRAM: | |
| return "BOOLEAN_HISTOGRAM"; | |
| case CUSTOM_HISTOGRAM: | |
| return "CUSTOM_HISTOGRAM"; | |
| case SPARSE_HISTOGRAM: | |
| return "SPARSE_HISTOGRAM"; | |
| case DUMMY_HISTOGRAM: | |
| return "DUMMY_HISTOGRAM"; | |
| } | |
| NOTREACHED(); | |
| return "UNKNOWN"; | |
| } | |
| HistogramBase* DeserializeHistogramInfo(PickleIterator* iter) { | |
| int type; | |
| if (!iter->ReadInt(&type)) | |
| return nullptr; | |
| 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 nullptr; | |
| } | |
| } | |
| const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; | |
| HistogramBase::HistogramBase(const char* name) | |
| : histogram_name_(name), flags_(kNoFlags) {} | |
| HistogramBase::~HistogramBase() = default; | |
| void HistogramBase::CheckName(const StringPiece& name) const { | |
| DCHECK_EQ(StringPiece(histogram_name()), name); | |
| } | |
| void HistogramBase::SetFlags(int32_t flags) { | |
| HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_); | |
| subtle::NoBarrier_Store(&flags_, old_flags | flags); | |
| } | |
| void HistogramBase::ClearFlags(int32_t flags) { | |
| HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_); | |
| subtle::NoBarrier_Store(&flags_, old_flags & ~flags); | |
| } | |
| void HistogramBase::AddScaled(Sample value, int count, int scale) { | |
| DCHECK_LT(0, scale); | |
| // Convert raw count and probabilistically round up/down if the remainder | |
| // is more than a random number [0, scale). This gives a more accurate | |
| // count when there are a large number of records. RandInt is "inclusive", | |
| // hence the -1 for the max value. | |
| int64_t count_scaled = count / scale; | |
| if (count - (count_scaled * scale) > base::RandInt(0, scale - 1)) | |
| count_scaled += 1; | |
| if (count_scaled == 0) | |
| return; | |
| AddCount(value, count_scaled); | |
| } | |
| void HistogramBase::AddKilo(Sample value, int count) { | |
| AddScaled(value, count, 1000); | |
| } | |
| void HistogramBase::AddKiB(Sample value, int count) { | |
| AddScaled(value, count, 1024); | |
| } | |
| void HistogramBase::AddTimeMillisecondsGranularity(const TimeDelta& time) { | |
| Add(saturated_cast<Sample>(time.InMilliseconds())); | |
| } | |
| void HistogramBase::AddTimeMicrosecondsGranularity(const TimeDelta& time) { | |
| // Intentionally drop high-resolution reports on clients with low-resolution | |
| // clocks. High-resolution metrics cannot make use of low-resolution data and | |
| // reporting it merely adds noise to the metric. https://crbug.com/807615#c16 | |
| if (TimeTicks::IsHighResolution()) | |
| Add(saturated_cast<Sample>(time.InMicroseconds())); | |
| } | |
| void HistogramBase::AddBoolean(bool value) { | |
| Add(value ? 1 : 0); | |
| } | |
| void HistogramBase::SerializeInfo(Pickle* pickle) const { | |
| pickle->WriteInt(GetHistogramType()); | |
| SerializeInfoImpl(pickle); | |
| } | |
| uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const { | |
| // Not supported by default. | |
| return NO_INCONSISTENCIES; | |
| } | |
| void HistogramBase::ValidateHistogramContents() const {} | |
| void HistogramBase::WriteJSON(std::string* output, | |
| JSONVerbosityLevel verbosity_level) const { | |
| Count count; | |
| int64_t sum; | |
| std::unique_ptr<ListValue> buckets(new ListValue()); | |
| GetCountAndBucketData(&count, &sum, buckets.get()); | |
| std::unique_ptr<DictionaryValue> parameters(new DictionaryValue()); | |
| GetParameters(parameters.get()); | |
| JSONStringValueSerializer serializer(output); | |
| DictionaryValue root; | |
| root.SetString("name", histogram_name()); | |
| root.SetInteger("count", count); | |
| root.SetDouble("sum", static_cast<double>(sum)); | |
| root.SetInteger("flags", flags()); | |
| root.Set("params", std::move(parameters)); | |
| if (verbosity_level != JSON_VERBOSITY_LEVEL_OMIT_BUCKETS) | |
| root.Set("buckets", std::move(buckets)); | |
| root.SetInteger("pid", GetUniqueIdForProcess()); | |
| serializer.Serialize(root); | |
| } | |
| void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const { | |
| if ((flags() & kCallbackExists) == 0) | |
| return; | |
| StatisticsRecorder::OnSampleCallback cb = | |
| StatisticsRecorder::FindCallback(histogram_name()); | |
| if (!cb.is_null()) | |
| cb.Run(sample); | |
| } | |
| void HistogramBase::WriteAsciiBucketGraph(double current_size, | |
| double max_size, | |
| std::string* output) const { | |
| const int k_line_length = 72; // Maximal horizontal width of graph. | |
| int x_count = static_cast<int>(k_line_length * (current_size / max_size) | |
| + 0.5); | |
| int x_remainder = k_line_length - x_count; | |
| while (0 < x_count--) | |
| output->append("-"); | |
| output->append("O"); | |
| while (0 < x_remainder--) | |
| output->append(" "); | |
| } | |
| const std::string HistogramBase::GetSimpleAsciiBucketRange( | |
| Sample sample) const { | |
| return StringPrintf("%d", sample); | |
| } | |
| void HistogramBase::WriteAsciiBucketValue(Count current, | |
| double scaled_sum, | |
| std::string* output) const { | |
| StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); | |
| } | |
| // static | |
| char const* HistogramBase::GetPermanentName(const std::string& name) { | |
| // A set of histogram names that provides the "permanent" lifetime required | |
| // by histogram objects for those strings that are not already code constants | |
| // or held in persistent memory. | |
| static LazyInstance<std::set<std::string>>::Leaky permanent_names; | |
| static LazyInstance<Lock>::Leaky permanent_names_lock; | |
| AutoLock lock(permanent_names_lock.Get()); | |
| auto result = permanent_names.Get().insert(name); | |
| return result.first->c_str(); | |
| } | |
| } // namespace base |