| // 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 <algorithm> |
| |
| #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<SampleInfo> 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<SampleInfo>(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<int, int> 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<Histogram*>(it->second.get()); |
| |
| RtcHistogram* hist = new RtcHistogram(name, min, max, bucket_count); |
| map_[name].reset(hist); |
| return reinterpret_cast<Histogram*>(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<Histogram*>(it->second.get()); |
| |
| RtcHistogram* hist = new RtcHistogram(name, 1, boundary, boundary + 1); |
| map_[name].reset(hist); |
| return reinterpret_cast<Histogram*>(hist); |
| } |
| |
| void GetAndReset( |
| std::map<std::string, std::unique_ptr<SampleInfo>>* histograms) { |
| MutexLock lock(&mutex_); |
| for (const auto& kv : map_) { |
| std::unique_ptr<SampleInfo> 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<int, int> Samples(const std::string& name) const { |
| MutexLock lock(&mutex_); |
| const auto& it = map_.find(name); |
| return (it == map_.end()) ? std::map<int, int>() : it->second->Samples(); |
| } |
| |
| private: |
| mutable Mutex mutex_; |
| std::map<std::string, std::unique_ptr<RtcHistogram>> 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<RtcHistogramMap*>(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<RtcHistogram*>(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<std::string, std::unique_ptr<SampleInfo>>* 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<int, int> Samples(const std::string& name) { |
| RtcHistogramMap* map = GetMap(); |
| return map ? map->Samples(name) : std::map<int, int>(); |
| } |
| |
| } // namespace metrics |
| } // namespace webrtc |