Index: webrtc/system_wrappers/source/metrics_default.cc |
diff --git a/webrtc/system_wrappers/source/metrics_default.cc b/webrtc/system_wrappers/source/metrics_default.cc |
index 48c9111e1b817397d91d7b140055fadbdcbdb75a..34d33a2179cf669a204a16d2176b4899a626e698 100644 |
--- a/webrtc/system_wrappers/source/metrics_default.cc |
+++ b/webrtc/system_wrappers/source/metrics_default.cc |
@@ -7,6 +7,10 @@ |
// be found in the AUTHORS file in the root of the source tree. |
// |
+#include "webrtc/system_wrappers/include/metrics_default.h" |
+ |
+#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 |
@@ -14,16 +18,277 @@ |
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 = 2000; |
+ |
+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) { |
+ if (sample < min_) |
+ sample = min_ - 1; // Underflow bucket. |
+ if (sample > max_) |
+ sample = max_; |
+ |
+ 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); |
+ copy->samples = info_.samples; |
+ info_.samples.clear(); |
+ 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); |
+ } |
-Histogram* HistogramFactoryGetCounts(const std::string& name, int min, int max, |
- int bucket_count) { return NULL; } |
+ 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) { |
+ 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) { return NULL; } |
+ int boundary) { |
+ RtcHistogramMap* map = GetMap(); |
+ if (!map) |
+ return nullptr; |
+ |
+ return map->GetEnumerationHistogram(name, boundary); |
+} |
-void HistogramAdd( |
- Histogram* histogram_pointer, const std::string& name, int sample) {} |
+// Fast path. Adds |sample| to cached |histogram_pointer|. |
+void HistogramAdd(Histogram* histogram_pointer, |
+ const std::string& name, |
+ int sample) { |
+ if (!histogram_pointer) |
+ return; |
+ |
+ RtcHistogram* ptr = reinterpret_cast<RtcHistogram*>(histogram_pointer); |
+ RTC_DCHECK_EQ(name, ptr->name()) << "The name should not vary."; |
+ 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 |
- |