|  | // 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 "webrtc/system_wrappers/include/metrics_default.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include "webrtc/base/criticalsection.h" | 
|  | #include "webrtc/base/thread_annotations.h" | 
|  | #include "webrtc/system_wrappers/include/metrics.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. | 
|  |  | 
|  | rtc::CritScope cs(&crit_); | 
|  | 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() { | 
|  | rtc::CritScope cs(&crit_); | 
|  | 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() { | 
|  | rtc::CritScope cs(&crit_); | 
|  | info_.samples.clear(); | 
|  | } | 
|  |  | 
|  | int NumEvents(int sample) const { | 
|  | rtc::CritScope cs(&crit_); | 
|  | const auto it = info_.samples.find(sample); | 
|  | return (it == info_.samples.end()) ? 0 : it->second; | 
|  | } | 
|  |  | 
|  | int NumSamples() const { | 
|  | int num_samples = 0; | 
|  | rtc::CritScope cs(&crit_); | 
|  | for (const auto& sample : info_.samples) { | 
|  | num_samples += sample.second; | 
|  | } | 
|  | return num_samples; | 
|  | } | 
|  |  | 
|  | int MinSample() const { | 
|  | rtc::CritScope cs(&crit_); | 
|  | return (info_.samples.empty()) ? -1 : info_.samples.begin()->first; | 
|  | } | 
|  |  | 
|  | private: | 
|  | rtc::CriticalSection crit_; | 
|  | const int min_; | 
|  | const int max_; | 
|  | SampleInfo info_ GUARDED_BY(crit_); | 
|  |  | 
|  | RTC_DISALLOW_COPY_AND_ASSIGN(RtcHistogram); | 
|  | }; | 
|  |  | 
|  | class RtcHistogramMap { | 
|  | public: | 
|  | RtcHistogramMap() {} | 
|  | ~RtcHistogramMap() {} | 
|  |  | 
|  | Histogram* GetCountsHistogram(const std::string& name, | 
|  | int min, | 
|  | int max, | 
|  | int bucket_count) { | 
|  | rtc::CritScope cs(&crit_); | 
|  | 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) { | 
|  | rtc::CritScope cs(&crit_); | 
|  | 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) { | 
|  | rtc::CritScope cs(&crit_); | 
|  | 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() { | 
|  | rtc::CritScope cs(&crit_); | 
|  | for (const auto& kv : map_) | 
|  | kv.second->Reset(); | 
|  | } | 
|  |  | 
|  | int NumEvents(const std::string& name, int sample) const { | 
|  | rtc::CritScope cs(&crit_); | 
|  | const auto& it = map_.find(name); | 
|  | return (it == map_.end()) ? 0 : it->second->NumEvents(sample); | 
|  | } | 
|  |  | 
|  | int NumSamples(const std::string& name) const { | 
|  | rtc::CritScope cs(&crit_); | 
|  | const auto& it = map_.find(name); | 
|  | return (it == map_.end()) ? 0 : it->second->NumSamples(); | 
|  | } | 
|  |  | 
|  | int MinSample(const std::string& name) const { | 
|  | rtc::CritScope cs(&crit_); | 
|  | const auto& it = map_.find(name); | 
|  | return (it == map_.end()) ? -1 : it->second->MinSample(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | rtc::CriticalSection crit_; | 
|  | std::map<std::string, std::unique_ptr<RtcHistogram>> map_ GUARDED_BY(crit_); | 
|  |  | 
|  | 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 | 
|  |  | 
|  | // 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); | 
|  | } | 
|  |  | 
|  | const std::string& GetHistogramName(Histogram* histogram_pointer) { | 
|  | RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer); | 
|  | return ptr->name(); | 
|  | } | 
|  |  | 
|  | // 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); | 
|  | } | 
|  |  | 
|  | 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_default.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; | 
|  | } | 
|  |  | 
|  | }  // namespace metrics | 
|  | }  // namespace webrtc |