// 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 "absl/strings/string_view.h" #include "rtc_base/string_utils.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(absl::string_view name, int min, int max, int bucket_count) : min_(min), max_(max), info_(name, min, max, bucket_count) { RTC_DCHECK_GT(bucket_count, 0); } RtcHistogram(const RtcHistogram&) = delete; RtcHistogram& operator=(const RtcHistogram&) = delete; 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_); }; class RtcHistogramMap { public: RtcHistogramMap() {} ~RtcHistogramMap() {} RtcHistogramMap(const RtcHistogramMap&) = delete; RtcHistogramMap& operator=(const RtcHistogramMap&) = delete; Histogram* GetCountsHistogram(absl::string_view 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_.emplace(name, hist); return reinterpret_cast(hist); } Histogram* GetEnumerationHistogram(absl::string_view 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_.emplace(name, hist); return reinterpret_cast(hist); } void GetAndReset(std::map, rtc::AbslStringViewCmp>* 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(absl::string_view name, int sample) const { MutexLock lock(&mutex_); const auto& it = map_.find(name); return (it == map_.end()) ? 0 : it->second->NumEvents(sample); } int NumSamples(absl::string_view name) const { MutexLock lock(&mutex_); const auto& it = map_.find(name); return (it == map_.end()) ? 0 : it->second->NumSamples(); } int MinSample(absl::string_view name) const { MutexLock lock(&mutex_); const auto& it = map_.find(name); return (it == map_.end()) ? -1 : it->second->MinSample(); } std::map Samples(absl::string_view 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, rtc::AbslStringViewCmp> map_ RTC_GUARDED_BY(mutex_); }; // 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 std::atomic g_rtc_histogram_map(nullptr); void CreateMap() { RtcHistogramMap* map = g_rtc_histogram_map.load(std::memory_order_acquire); if (map == nullptr) { RtcHistogramMap* new_map = new RtcHistogramMap(); if (!g_rtc_histogram_map.compare_exchange_strong(map, new_map)) 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 std::atomic g_rtc_histogram_called(0); #endif // Gets the map (or nullptr). RtcHistogramMap* GetMap() { #if RTC_DCHECK_IS_ON g_rtc_histogram_called.store(1, std::memory_order_release); #endif return g_rtc_histogram_map.load(); } } // 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(absl::string_view 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(absl::string_view 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(absl::string_view 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(absl::string_view 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(absl::string_view 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.load() == nullptr); #if RTC_DCHECK_IS_ON RTC_DCHECK_EQ(0, g_rtc_histogram_called.load(std::memory_order_acquire)); #endif CreateMap(); } void GetAndReset( std::map, rtc::AbslStringViewCmp>* histograms) { histograms->clear(); RtcHistogramMap* map = GetMap(); if (map) map->GetAndReset(histograms); } void Reset() { RtcHistogramMap* map = GetMap(); if (map) map->Reset(); } int NumEvents(absl::string_view name, int sample) { RtcHistogramMap* map = GetMap(); return map ? map->NumEvents(name, sample) : 0; } int NumSamples(absl::string_view name) { RtcHistogramMap* map = GetMap(); return map ? map->NumSamples(name) : 0; } int MinSample(absl::string_view name) { RtcHistogramMap* map = GetMap(); return map ? map->MinSample(name) : -1; } std::map Samples(absl::string_view name) { RtcHistogramMap* map = GetMap(); return map ? map->Samples(name) : std::map(); } } // namespace metrics } // namespace webrtc