// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // #include "system_wrappers/include/metrics.h" #include #include "rtc_base/constructor_magic.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" // Default implementation of histogram methods for WebRTC clients that do not // want to provide their own implementation. namespace webrtc { namespace metrics { class Histogram; namespace { // Limit for the maximum number of sample values that can be stored. // TODO(asapersson): Consider using bucket count (and set up // linearly/exponentially spaced buckets) if samples are logged more frequently. const int kMaxSampleMapSize = 300; class RtcHistogram { public: RtcHistogram(const std::string& name, int min, int max, int bucket_count) : min_(min), max_(max), info_(name, min, max, bucket_count) { RTC_DCHECK_GT(bucket_count, 0); } void Add(int sample) { sample = std::min(sample, max_); sample = std::max(sample, min_ - 1); // Underflow bucket. MutexLock lock(&mutex_); if (info_.samples.size() == kMaxSampleMapSize && info_.samples.find(sample) == info_.samples.end()) { return; } ++info_.samples[sample]; } // Returns a copy (or nullptr if there are no samples) and clears samples. std::unique_ptr GetAndReset() { MutexLock lock(&mutex_); if (info_.samples.empty()) return nullptr; SampleInfo* copy = new SampleInfo(info_.name, info_.min, info_.max, info_.bucket_count); std::swap(info_.samples, copy->samples); return std::unique_ptr(copy); } const std::string& name() const { return info_.name; } // Functions only for testing. void Reset() { MutexLock lock(&mutex_); info_.samples.clear(); } int NumEvents(int sample) const { MutexLock lock(&mutex_); const auto it = info_.samples.find(sample); return (it == info_.samples.end()) ? 0 : it->second; } int NumSamples() const { int num_samples = 0; MutexLock lock(&mutex_); for (const auto& sample : info_.samples) { num_samples += sample.second; } return num_samples; } int MinSample() const { MutexLock lock(&mutex_); return (info_.samples.empty()) ? -1 : info_.samples.begin()->first; } std::map Samples() const { MutexLock lock(&mutex_); return info_.samples; } private: mutable Mutex mutex_; const int min_; const int max_; SampleInfo info_ RTC_GUARDED_BY(mutex_); RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram); }; class RtcHistogramMap { public: RtcHistogramMap() {} ~RtcHistogramMap() {} Histogram* GetCountsHistogram(const std::string& name, int min, int max, int bucket_count) { MutexLock lock(&mutex_); const auto& it = map_.find(name); if (it != map_.end()) return reinterpret_cast(it->second.get()); RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count); map_[name].reset(hist); return reinterpret_cast(hist); } Histogram* GetEnumerationHistogram(const std::string& name, int boundary) { MutexLock lock(&mutex_); const auto& it = map_.find(name); if (it != map_.end()) return reinterpret_cast(it->second.get()); RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1); map_[name].reset(hist); return reinterpret_cast(hist); } void GetAndReset( std::map>* histograms) { MutexLock lock(&mutex_); for (const auto& kv : map_) { std::unique_ptr info = kv.second->GetAndReset(); if (info) histograms->insert(std::make_pair(kv.first, std::move(info))); } } // Functions only for testing. void Reset() { MutexLock lock(&mutex_); for (const auto& kv : map_) kv.second->Reset(); } int NumEvents(const std::string& name, int sample) const { MutexLock lock(&mutex_); const auto& it = map_.find(name); return (it == map_.end()) ? 0 : it->second->NumEvents(sample); } int NumSamples(const std::string& name) const { MutexLock lock(&mutex_); const auto& it = map_.find(name); return (it == map_.end()) ? 0 : it->second->NumSamples(); } int MinSample(const std::string& name) const { MutexLock lock(&mutex_); const auto& it = map_.find(name); return (it == map_.end()) ? -1 : it->second->MinSample(); } std::map Samples(const std::string& name) const { MutexLock lock(&mutex_); const auto& it = map_.find(name); return (it == map_.end()) ? std::map() : it->second->Samples(); } private: mutable Mutex mutex_; std::map> map_ RTC_GUARDED_BY(mutex_); RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogramMap); }; // RtcHistogramMap is allocated upon call to Enable(). // The histogram getter functions, which return pointer values to the histograms // in the map, are cached in WebRTC. Therefore, this memory is not freed by the // application (the memory will be reclaimed by the OS). static RtcHistogramMap* volatile g_rtc_histogram_map = nullptr; void CreateMap() { RtcHistogramMap* map = rtc::AtomicOps::AcquireLoadPtr(&g_rtc_histogram_map); if (map == nullptr) { RtcHistogramMap* new_map = new RtcHistogramMap(); RtcHistogramMap* old_map = rtc::AtomicOps::CompareAndSwapPtr( &g_rtc_histogram_map, static_cast(nullptr), new_map); if (old_map != nullptr) delete new_map; } } // Set the first time we start using histograms. Used to make sure Enable() is // not called thereafter. #if RTC_DCHECK_IS_ON static volatile int g_rtc_histogram_called = 0; #endif // Gets the map (or nullptr). RtcHistogramMap* GetMap() { #if RTC_DCHECK_IS_ON rtc::AtomicOps::ReleaseStore(&g_rtc_histogram_called, 1); #endif return g_rtc_histogram_map; } } // namespace #ifndef WEBRTC_EXCLUDE_METRICS_DEFAULT // Implementation of histogram methods in // webrtc/system_wrappers/interface/metrics.h. // Histogram with exponentially spaced buckets. // Creates (or finds) histogram. // The returned histogram pointer is cached (and used for adding samples in // subsequent calls). Histogram* HistogramFactoryGetCounts(const std::string& name, int min, int max, int bucket_count) { // TODO(asapersson): Alternative implementation will be needed if this // histogram type should be truly exponential. return HistogramFactoryGetCountsLinear(name, min, max, bucket_count); } // Histogram with linearly spaced buckets. // Creates (or finds) histogram. // The returned histogram pointer is cached (and used for adding samples in // subsequent calls). Histogram* HistogramFactoryGetCountsLinear(const std::string& name, int min, int max, int bucket_count) { RtcHistogramMap* map = GetMap(); if (!map) return nullptr; return map->GetCountsHistogram(name, min, max, bucket_count); } // Histogram with linearly spaced buckets. // Creates (or finds) histogram. // The returned histogram pointer is cached (and used for adding samples in // subsequent calls). Histogram* HistogramFactoryGetEnumeration(const std::string& name, int boundary) { RtcHistogramMap* map = GetMap(); if (!map) return nullptr; return map->GetEnumerationHistogram(name, boundary); } // Our default implementation reuses the non-sparse histogram. Histogram* SparseHistogramFactoryGetEnumeration(const std::string& name, int boundary) { return HistogramFactoryGetEnumeration(name, boundary); } // Fast path. Adds |sample| to cached |histogram_pointer|. void HistogramAdd(Histogram* histogram_pointer, int sample) { RtcHistogram* ptr = reinterpret_cast(histogram_pointer); ptr->Add(sample); } #endif // WEBRTC_EXCLUDE_METRICS_DEFAULT SampleInfo::SampleInfo(const std::string& name, int min, int max, size_t bucket_count) : name(name), min(min), max(max), bucket_count(bucket_count) {} SampleInfo::~SampleInfo() {} // Implementation of global functions in metrics.h. void Enable() { RTC_DCHECK(g_rtc_histogram_map == nullptr); #if RTC_DCHECK_IS_ON RTC_DCHECK_EQ(0, rtc::AtomicOps::AcquireLoad(&g_rtc_histogram_called)); #endif CreateMap(); } void GetAndReset( std::map>* histograms) { histograms->clear(); RtcHistogramMap* map = GetMap(); if (map) map->GetAndReset(histograms); } void Reset() { RtcHistogramMap* map = GetMap(); if (map) map->Reset(); } int NumEvents(const std::string& name, int sample) { RtcHistogramMap* map = GetMap(); return map ? map->NumEvents(name, sample) : 0; } int NumSamples(const std::string& name) { RtcHistogramMap* map = GetMap(); return map ? map->NumSamples(name) : 0; } int MinSample(const std::string& name) { RtcHistogramMap* map = GetMap(); return map ? map->MinSample(name) : -1; } std::map Samples(const std::string& name) { RtcHistogramMap* map = GetMap(); return map ? map->Samples(name) : std::map(); } } // namespace metrics } // namespace webrtc