diff --git a/all.gyp b/all.gyp index c36bbfe586..da54e724d4 100644 --- a/all.gyp +++ b/all.gyp @@ -28,6 +28,7 @@ 'webrtc/modules/modules.gyp:*', 'webrtc/p2p/p2p.gyp:*', 'webrtc/pc/pc.gyp:*', + 'webrtc/stats/stats.gyp:*', 'webrtc/system_wrappers/system_wrappers.gyp:*', 'webrtc/tools/tools.gyp:*', 'webrtc/voice_engine/voice_engine.gyp:*', diff --git a/webrtc/BUILD.gn b/webrtc/BUILD.gn index 9f75117af4..e30fd6b49f 100644 --- a/webrtc/BUILD.gn +++ b/webrtc/BUILD.gn @@ -259,6 +259,7 @@ if (!is_ios || !build_with_chromium) { "common_audio", "common_video", "modules", + "stats", "system_wrappers", "tools", "video", diff --git a/webrtc/api/BUILD.gn b/webrtc/api/BUILD.gn index d06439f433..a1a4a86217 100644 --- a/webrtc/api/BUILD.gn +++ b/webrtc/api/BUILD.gn @@ -69,6 +69,8 @@ source_set("libjingle_peerconnection") { "proxy.h", "remoteaudiosource.cc", "remoteaudiosource.h", + "rtcstats.h", + "rtcstatsreport.h", "rtpparameters.h", "rtpreceiver.cc", "rtpreceiver.h", diff --git a/webrtc/api/api.gyp b/webrtc/api/api.gyp index 86a55fd9d2..f9f846b544 100644 --- a/webrtc/api/api.gyp +++ b/webrtc/api/api.gyp @@ -144,6 +144,8 @@ 'proxy.h', 'remoteaudiosource.cc', 'remoteaudiosource.h', + 'rtcstats.h', + 'rtcstatsreport.h', 'rtpparameters.h', 'rtpreceiver.cc', 'rtpreceiver.h', diff --git a/webrtc/api/rtcstats.h b/webrtc/api/rtcstats.h new file mode 100644 index 0000000000..4b2d70ff97 --- /dev/null +++ b/webrtc/api/rtcstats.h @@ -0,0 +1,283 @@ +/* + * Copyright 2016 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. + */ + +#ifndef WEBRTC_API_RTCSTATS_H_ +#define WEBRTC_API_RTCSTATS_H_ + +#include +#include +#include +#include +#include + +#include "webrtc/base/checks.h" + +namespace webrtc { + +class RTCStatsMemberInterface; + +// Abstract base class for RTCStats-derived dictionaries, see +// https://w3c.github.io/webrtc-stats/. +// +// All derived classes must have the following static variable defined: +// static const char kType[]; +// It is used as a unique class identifier and a string representation of the +// class type, see https://w3c.github.io/webrtc-stats/#rtcstatstype-str*. +// Use the |WEBRTC_RTCSTATS_IMPL| macro when implementing subclasses, see macro +// for details. +// +// Derived classes list their dictionary members, RTCStatsMember, as public +// fields, allowing the following: +// +// RTCFooStats foo("fooId", GetCurrentTime()); +// foo.bar = 42; +// foo.baz = std::vector(); +// foo.baz->push_back("hello world"); +// uint32_t x = *foo.bar; +// +// Pointers to all the members are available with |Members|, allowing iteration: +// +// for (const RTCStatsMemberInterface* member : foo.Members()) { +// printf("%s = %s\n", member->name(), member->ValueToString().c_str()); +// } +class RTCStats { + public: + RTCStats(const std::string& id, double timestamp) + : id_(id), timestamp_(timestamp) {} + RTCStats(std::string&& id, double timestamp) + : id_(std::move(id)), timestamp_(timestamp) {} + virtual ~RTCStats() {} + + virtual std::unique_ptr copy() const = 0; + + const std::string& id() const { return id_; } + // Time relative to the UNIX epoch (Jan 1, 1970, UTC), in seconds. + double timestamp() const { return timestamp_; } + // Returns the static member variable |kType| of the implementing class. + virtual const char* type() const = 0; + // Returns a vector of pointers to all the RTCStatsMemberInterface members of + // this class. This allows for iteration of members. + std::vector Members() const; + + // Creates a human readable string representation of the report, listing all + // of its members (names and values). + std::string ToString() const; + + // Downcasts the stats object to an |RTCStats| subclass |T|. DCHECKs that the + // object is of type |T|. + template + const T& cast_to() const { + RTC_DCHECK_EQ(type(), T::kType); + return static_cast(*this); + } + + protected: + // Gets a vector of all members of this |RTCStats| object, including members + // derived from parent classes. |additional_capacity| is how many more members + // shall be reserved in the vector (so that subclasses can allocate a vector + // with room for both parent and child members without it having to resize). + virtual std::vector + MembersOfThisObjectAndAncestors( + size_t additional_capacity) const; + + std::string const id_; + double timestamp_; +}; + +// All |RTCStats| classes should use this macro in a public section of the class +// definition. +// +// This macro declares the static |kType| and overrides methods as required by +// subclasses of |RTCStats|: |copy|, |type|, and +// |MembersOfThisObjectAndAncestors|. The |...| argument is a list of addresses +// to each member defined in the implementing class (list cannot be empty, must +// have at least one new member). +// +// (Since class names need to be known to implement these methods this cannot be +// part of the base |RTCStats|. While these methods could be implemented using +// templates, that would only work for immediate subclasses. Subclasses of +// subclasses also have to override these methods, resulting in boilerplate +// code. Using a macro avoids this and works for any |RTCStats| class, including +// grandchildren.) +// +// Sample usage: +// +// rtcfoostats.h: +// class RTCFooStats : public RTCStats { +// public: +// RTCFooStats(const std::string& id, double timestamp) +// : RTCStats(id, timestamp), +// foo("foo"), +// bar("bar") { +// } +// +// WEBRTC_RTCSTATS_IMPL(RTCStats, RTCFooStats, +// &foo, +// &bar); +// +// RTCStatsMember foo; +// RTCStatsMember bar; +// }; +// +// rtcfoostats.cc: +// const char RTCFooStats::kType[] = "foo-stats"; +// +#define WEBRTC_RTCSTATS_IMPL(parent_class, this_class, ...) \ + public: \ + static const char kType[]; \ + std::unique_ptr copy() const override { \ + return std::unique_ptr(new this_class(*this)); \ + } \ + const char* type() const override { return this_class::kType; } \ + protected: \ + std::vector \ + MembersOfThisObjectAndAncestors( \ + size_t local_var_additional_capacity) const override { \ + const webrtc::RTCStatsMemberInterface* local_var_members[] = { \ + __VA_ARGS__ \ + }; \ + size_t local_var_members_count = \ + sizeof(local_var_members) / sizeof(local_var_members[0]); \ + std::vector local_var_members_vec =\ + parent_class::MembersOfThisObjectAndAncestors( \ + local_var_members_count + local_var_additional_capacity); \ + RTC_DCHECK_GE( \ + local_var_members_vec.capacity() - local_var_members_vec.size(), \ + local_var_members_count + local_var_additional_capacity); \ + local_var_members_vec.insert(local_var_members_vec.end(), \ + &local_var_members[0], \ + &local_var_members[local_var_members_count]); \ + return local_var_members_vec; \ + } \ + public: + +// Interface for |RTCStats| members, which have a name and a value of a type +// defined in a subclass. Only the types listed in |Type| are supported, these +// are implemented by |RTCStatsMember|. The value of a member may be +// undefined, the value can only be read if |is_defined|. +class RTCStatsMemberInterface { + public: + // Member value types. + enum Type { + kInt32, // int32_t + kUint32, // uint32_t + kInt64, // int64_t + kUint64, // uint64_t + kDouble, // double + kStaticString, // const char* + kString, // std::string + + kSequenceInt32, // std::vector + kSequenceUint32, // std::vector + kSequenceInt64, // std::vector + kSequenceUint64, // std::vector + kSequenceDouble, // std::vector + kSequenceStaticString, // std::vector + kSequenceString, // std::vector + }; + + virtual ~RTCStatsMemberInterface() {} + + const char* name() const { return name_; } + virtual Type type() const = 0; + virtual bool is_sequence() const = 0; + virtual bool is_string() const = 0; + bool is_defined() const { return is_defined_; } + virtual std::string ValueToString() const = 0; + + template + const T& cast_to() const { + RTC_DCHECK_EQ(type(), T::kType); + return static_cast(*this); + } + + protected: + RTCStatsMemberInterface(const char* name, bool is_defined) + : name_(name), is_defined_(is_defined) {} + + const char* const name_; + bool is_defined_; +}; + +// Template implementation of |RTCStatsMemberInterface|. Every possible |T| is +// specialized in rtcstats.cc, using a different |T| results in a linker error +// (undefined reference to |kType|). The supported types are the ones described +// by |RTCStatsMemberInterface::Type|. +template +class RTCStatsMember : public RTCStatsMemberInterface { + public: + static const Type kType; + + explicit RTCStatsMember(const char* name) + : RTCStatsMemberInterface(name, false), + value_() {} + RTCStatsMember(const char* name, const T& value) + : RTCStatsMemberInterface(name, true), + value_(value) {} + RTCStatsMember(const char* name, T&& value) + : RTCStatsMemberInterface(name, true), + value_(std::move(value)) {} + explicit RTCStatsMember(const RTCStatsMember& other) + : RTCStatsMemberInterface(other.name_, other.is_defined_), + value_(other.value_) {} + explicit RTCStatsMember(RTCStatsMember&& other) + : RTCStatsMemberInterface(other.name_, other.is_defined_), + value_(std::move(other.value_)) {} + + Type type() const override { return kType; } + bool is_sequence() const override; + bool is_string() const override; + std::string ValueToString() const override; + + // Assignment operators. + T& operator=(const T& value) { + value_ = value; + is_defined_ = true; + return value_; + } + T& operator=(const T&& value) { + value_ = std::move(value); + is_defined_ = true; + return value_; + } + T& operator=(const RTCStatsMember& other) { + RTC_DCHECK(other.is_defined_); + value_ = other.is_defined_; + is_defined_ = true; + return value_; + } + + // Value getters. + T& operator*() { + RTC_DCHECK(is_defined_); + return value_; + } + const T& operator*() const { + RTC_DCHECK(is_defined_); + return value_; + } + + // Value getters, arrow operator. + T* operator->() { + RTC_DCHECK(is_defined_); + return &value_; + } + const T* operator->() const { + RTC_DCHECK(is_defined_); + return &value_; + } + + private: + T value_; +}; + +} // namespace webrtc + +#endif // WEBRTC_API_RTCSTATS_H_ diff --git a/webrtc/api/rtcstatsreport.h b/webrtc/api/rtcstatsreport.h new file mode 100644 index 0000000000..42237eba72 --- /dev/null +++ b/webrtc/api/rtcstatsreport.h @@ -0,0 +1,86 @@ +/* + * Copyright 2016 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. + */ + +#ifndef WEBRTC_API_RTCSTATSREPORT_H_ +#define WEBRTC_API_RTCSTATSREPORT_H_ + +#include +#include +#include +#include + +#include "webrtc/api/rtcstats.h" +#include "webrtc/base/refcount.h" +#include "webrtc/base/scoped_ref_ptr.h" + +namespace webrtc { + +// A collection of stats. +// This is accessible as a map from |RTCStats::id| to |RTCStats|. +class RTCStatsReport : public rtc::RefCountInterface { + public: + typedef std::map> StatsMap; + + class ConstIterator { + public: + ConstIterator(const ConstIterator&& other); + ~ConstIterator(); + + ConstIterator& operator++(); + ConstIterator& operator++(int); + const RTCStats& operator*() const; + bool operator==(const ConstIterator& other) const; + bool operator!=(const ConstIterator& other) const; + + private: + friend class RTCStatsReport; + ConstIterator(const rtc::scoped_refptr& report, + StatsMap::const_iterator it); + + // Reference report to make sure it is kept alive. + rtc::scoped_refptr report_; + StatsMap::const_iterator it_; + }; + + static rtc::scoped_refptr Create(); + + RTCStatsReport(); + RTCStatsReport(const RTCStatsReport& other) = delete; + + bool AddStats(std::unique_ptr stats); + const RTCStats* operator[](const std::string& id) const; + size_t size() const { return stats_.size(); } + + ConstIterator begin() const; + ConstIterator end() const; + + // Gets the subset of stats that are of type |T|, where |T| is any class + // descending from |RTCStats|. + template + std::vector GetStatsOfType() const { + std::vector stats_of_type; + for (const RTCStats& stats : *this) { + if (stats.type() == T::kType) + stats_of_type.push_back(&stats.cast_to()); + } + return stats_of_type; + } + + friend class rtc::RefCountedObject; + + private: + ~RTCStatsReport() override; + + StatsMap stats_; +}; + +} // namespace webrtc + +#endif // WEBRTC_API_RTCSTATSREPORT_H_ diff --git a/webrtc/stats/BUILD.gn b/webrtc/stats/BUILD.gn new file mode 100644 index 0000000000..3ef6188a18 --- /dev/null +++ b/webrtc/stats/BUILD.gn @@ -0,0 +1,56 @@ +# Copyright (c) 2016 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. + +import("../build/webrtc.gni") +import("//testing/test.gni") + +group("stats") { + deps = [ + ":rtc_stats", + ] +} + +# GYP version: webrtc/stats/stats.gyp:rtc_stats +source_set("rtc_stats") { + cflags = [] + sources = [ + "rtcstats.cc", + "rtcstatsreport.cc", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + deps = [ + "../api:libjingle_peerconnection", + ] +} + +if (rtc_include_tests) { + # GYP version: webrtc/stats/stats.gyp:rtc_stats_unittests + test("rtc_stats_unittests") { + testonly = true + sources = [ + "rtcstats_unittest.cc", + ] + + configs += [ "..:common_config" ] + public_configs = [ "..:common_inherited_config" ] + + deps = [ + ":rtc_stats", + "../base:rtc_base_tests_utils", + "../system_wrappers:metrics_default", + "//testing/gmock", + ] + + if (is_android) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + } + } +} diff --git a/webrtc/stats/DEPS b/webrtc/stats/DEPS new file mode 100644 index 0000000000..e4fbd6b2d2 --- /dev/null +++ b/webrtc/stats/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+webrtc/api", + "+webrtc/base", +] diff --git a/webrtc/stats/OWNERS b/webrtc/stats/OWNERS new file mode 100644 index 0000000000..7e98070d5d --- /dev/null +++ b/webrtc/stats/OWNERS @@ -0,0 +1,2 @@ +hbos@webrtc.org +hta@webrtc.org diff --git a/webrtc/stats/rtcstats.cc b/webrtc/stats/rtcstats.cc new file mode 100644 index 0000000000..1a35739c61 --- /dev/null +++ b/webrtc/stats/rtcstats.cc @@ -0,0 +1,132 @@ +/* + * Copyright 2016 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/api/rtcstats.h" + +#include "webrtc/base/stringencode.h" + +namespace webrtc { + +namespace { + +// Produces "{ a, b, c }". Works for non-vector |RTCStatsMemberInterface::Type| +// types. +template +std::string VectorToString(const std::vector& vector) { + if (vector.empty()) + return "{}"; + std::ostringstream oss; + oss << "{ " << rtc::ToString(vector[0]); + for (size_t i = 1; i < vector.size(); ++i) { + oss << ", " << rtc::ToString(vector[i]); + } + oss << " }"; + return oss.str(); +} + +// Produces "{ \"a\", \"b\", \"c\" }". Works for vectors of both const char* and +// std::string element types. +template +std::string VectorOfStringsToString(const std::vector& strings) { + if (strings.empty()) + return "{}"; + std::ostringstream oss; + oss << "{ \"" << rtc::ToString(strings[0]) << '\"'; + for (size_t i = 1; i < strings.size(); ++i) { + oss << ", \"" << rtc::ToString(strings[i]) << '\"'; + } + oss << " }"; + return oss.str(); +} + +} // namespace + +std::string RTCStats::ToString() const { + std::ostringstream oss; + oss << type() << " {\n id: \"" << id_ << "\"\n timestamp: " + << timestamp_ << '\n'; + for (const RTCStatsMemberInterface* member : Members()) { + oss << " " << member->name() << ": "; + if (member->is_defined()) { + if (member->is_string()) + oss << '"' << member->ValueToString() << "\"\n"; + else + oss << member->ValueToString() << '\n'; + } else { + oss << "undefined\n"; + } + } + oss << '}'; + return oss.str(); +} + +std::vector RTCStats::Members() const { + return MembersOfThisObjectAndAncestors(0); +} + +std::vector +RTCStats::MembersOfThisObjectAndAncestors( + size_t additional_capacity) const { + std::vector members; + members.reserve(additional_capacity); + return members; +} + +#define WEBRTC_DEFINE_RTCSTATSMEMBER(T, type, is_seq, is_str, to_str) \ + template<> \ + const RTCStatsMemberInterface::Type RTCStatsMember::kType = \ + RTCStatsMemberInterface::type; \ + template<> \ + bool RTCStatsMember::is_sequence() const { return is_seq; } \ + template<> \ + bool RTCStatsMember::is_string() const { return is_str; } \ + template<> \ + std::string RTCStatsMember::ValueToString() const { \ + RTC_DCHECK(is_defined_); \ + return to_str; \ + } + +WEBRTC_DEFINE_RTCSTATSMEMBER(int32_t, kInt32, false, false, + rtc::ToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER(uint32_t, kUint32, false, false, + rtc::ToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER(int64_t, kInt64, false, false, + rtc::ToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER(uint64_t, kUint64, false, false, + rtc::ToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER(double, kDouble, false, false, + rtc::ToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER(const char*, kStaticString, false, true, + value_); +WEBRTC_DEFINE_RTCSTATSMEMBER(std::string, kString, false, true, + value_); +WEBRTC_DEFINE_RTCSTATSMEMBER( + std::vector, kSequenceInt32, true, false, + VectorToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER( + std::vector, kSequenceUint32, true, false, + VectorToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER( + std::vector, kSequenceInt64, true, false, + VectorToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER( + std::vector, kSequenceUint64, true, false, + VectorToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER( + std::vector, kSequenceDouble, true, false, + VectorToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER( + std::vector, kSequenceStaticString, true, false, + VectorOfStringsToString(value_)); +WEBRTC_DEFINE_RTCSTATSMEMBER( + std::vector, kSequenceString, true, false, + VectorOfStringsToString(value_)); + +} // namespace webrtc diff --git a/webrtc/stats/rtcstats_unittest.cc b/webrtc/stats/rtcstats_unittest.cc new file mode 100644 index 0000000000..12364aed56 --- /dev/null +++ b/webrtc/stats/rtcstats_unittest.cc @@ -0,0 +1,219 @@ +/* + * Copyright 2016 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/api/rtcstats.h" +#include "webrtc/api/rtcstatsreport.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/gunit.h" +#include "webrtc/base/logging.h" + +namespace webrtc { + +class RTCTestStats : public RTCStats { + public: + RTCTestStats(const std::string& id, double timestamp) + : RTCStats(id, timestamp), + m_int32("mInt32"), + m_uint32("mUint32"), + m_int64("mInt64"), + m_uint64("mUint64"), + m_double("mDouble"), + m_static_string("mStaticString"), + m_string("mString"), + m_sequence_int32("mSequenceInt32"), + m_sequence_uint32("mSequenceUint32"), + m_sequence_int64("mSequenceInt64"), + m_sequence_uint64("mSequenceUint64"), + m_sequence_double("mSequenceDouble"), + m_sequence_static_string("mSequenceStaticString"), + m_sequence_string("mSequenceString") { + } + + WEBRTC_RTCSTATS_IMPL(RTCStats, RTCTestStats, + &m_int32, + &m_uint32, + &m_int64, + &m_uint64, + &m_double, + &m_static_string, + &m_string, + &m_sequence_int32, + &m_sequence_uint32, + &m_sequence_int64, + &m_sequence_uint64, + &m_sequence_double, + &m_sequence_static_string, + &m_sequence_string); + + RTCStatsMember m_int32; + RTCStatsMember m_uint32; + RTCStatsMember m_int64; + RTCStatsMember m_uint64; + RTCStatsMember m_double; + RTCStatsMember m_static_string; + RTCStatsMember m_string; + + RTCStatsMember> m_sequence_int32; + RTCStatsMember> m_sequence_uint32; + RTCStatsMember> m_sequence_int64; + RTCStatsMember> m_sequence_uint64; + RTCStatsMember> m_sequence_double; + RTCStatsMember> m_sequence_static_string; + RTCStatsMember> m_sequence_string; +}; + +const char RTCTestStats::kType[] = "test-stats"; + +class RTCChildStats : public RTCStats { + public: + RTCChildStats(const std::string& id, double timestamp) + : RTCStats(id, timestamp), + child_int("childInt") {} + + WEBRTC_RTCSTATS_IMPL(RTCStats, RTCChildStats, + &child_int); + + RTCStatsMember child_int; +}; + +const char RTCChildStats::kType[] = "child-stats"; + +class RTCGrandChildStats : public RTCChildStats { + public: + RTCGrandChildStats(const std::string& id, double timestamp) + : RTCChildStats(id, timestamp), + grandchild_int("grandchildInt") {} + + WEBRTC_RTCSTATS_IMPL(RTCChildStats, RTCGrandChildStats, + &grandchild_int); + + RTCStatsMember grandchild_int; +}; + +const char RTCGrandChildStats::kType[] = "grandchild-stats"; + +TEST(RTCStatsTest, RTCStatsAndMembers) { + RTCTestStats stats("testId", 42.0); + EXPECT_EQ(stats.id(), "testId"); + EXPECT_EQ(stats.timestamp(), 42.0); + std::vector members = stats.Members(); + EXPECT_EQ(members.size(), static_cast(14)); + for (const RTCStatsMemberInterface* member : members) { + EXPECT_FALSE(member->is_defined()); + } + stats.m_int32 = 123; + stats.m_uint32 = 123; + stats.m_int64 = 123; + stats.m_uint64 = 123; + stats.m_double = 123.0; + stats.m_static_string = "123"; + stats.m_string = std::string("123"); + stats.m_sequence_int32 = std::vector(); + stats.m_sequence_uint32 = std::vector(); + EXPECT_FALSE(stats.m_sequence_int64.is_defined()); + stats.m_sequence_int64 = std::vector(); + stats.m_sequence_uint64 = std::vector(); + stats.m_sequence_double = std::vector(); + stats.m_sequence_static_string = std::vector(); + stats.m_sequence_string = std::vector(); + for (const RTCStatsMemberInterface* member : members) { + EXPECT_TRUE(member->is_defined()); + } + EXPECT_EQ(*stats.m_int32, static_cast(123)); + EXPECT_EQ(*stats.m_uint32, static_cast(123)); + EXPECT_EQ(*stats.m_int64, static_cast(123)); + EXPECT_EQ(*stats.m_uint64, static_cast(123)); + EXPECT_EQ(*stats.m_double, 123.0); + EXPECT_EQ(*stats.m_static_string, "123"); + EXPECT_EQ(*stats.m_string, std::string("123")); + EXPECT_EQ(*stats.m_sequence_int32, std::vector()); + EXPECT_EQ(*stats.m_sequence_uint32, std::vector()); + EXPECT_EQ(*stats.m_sequence_int64, std::vector()); + EXPECT_EQ(*stats.m_sequence_uint64, std::vector()); + EXPECT_EQ(*stats.m_sequence_double, std::vector()); + EXPECT_EQ(*stats.m_sequence_static_string, std::vector()); + EXPECT_EQ(*stats.m_sequence_string, std::vector()); + int32_t numbers[] = { 4, 8, 15, 16, 23, 42 }; + std::vector numbers_sequence(&numbers[0], &numbers[5]); + stats.m_sequence_int32->insert(stats.m_sequence_int32->end(), + numbers_sequence.begin(), + numbers_sequence.end()); + EXPECT_EQ(*stats.m_sequence_int32, numbers_sequence); +} + +TEST(RTCStatsTest, RTCStatsGrandChild) { + RTCGrandChildStats stats("grandchild", 0.0); + stats.child_int = 1; + stats.grandchild_int = 2; + int32_t sum = 0; + for (const RTCStatsMemberInterface* member : stats.Members()) { + sum += *member->cast_to>(); + } + EXPECT_EQ(sum, static_cast(3)); + + std::unique_ptr copy_ptr = stats.copy(); + const RTCGrandChildStats& copy = copy_ptr->cast_to(); + EXPECT_EQ(*copy.child_int, *stats.child_int); + EXPECT_EQ(*copy.grandchild_int, *stats.grandchild_int); +} + +TEST(RTCStatsTest, TestRTCStatsReport) { + rtc::scoped_refptr report = RTCStatsReport::Create(); + EXPECT_EQ(report->size(), static_cast(0)); + report->AddStats(std::unique_ptr(new RTCTestStats("a0", 1.0))); + report->AddStats(std::unique_ptr(new RTCTestStats("a1", 2.0))); + report->AddStats(std::unique_ptr(new RTCChildStats("b0", 4.0))); + report->AddStats(std::unique_ptr(new RTCChildStats("b1", 8.0))); + report->AddStats(std::unique_ptr(new RTCTestStats("a2", 16.0))); + report->AddStats(std::unique_ptr(new RTCChildStats("b2", 32.0))); + EXPECT_EQ(report->size(), static_cast(6)); + + EXPECT_EQ((*report)["missing"], nullptr); + EXPECT_EQ((*report)["a0"]->id(), "a0"); + EXPECT_EQ((*report)["b2"]->id(), "b2"); + + std::vector a = report->GetStatsOfType(); + EXPECT_EQ(a.size(), static_cast(3)); + uint32_t mask = 0; + for (const RTCTestStats* stats : a) + mask |= static_cast(stats->timestamp()); + EXPECT_EQ(mask, static_cast(1 | 2 | 16)); + + std::vector b = report->GetStatsOfType(); + EXPECT_EQ(b.size(), static_cast(3)); + mask = 0; + for (const RTCChildStats* stats : b) + mask |= static_cast(stats->timestamp()); + EXPECT_EQ(mask, static_cast(4 | 8 | 32)); + + EXPECT_EQ(report->GetStatsOfType().size(), + static_cast(0)); +} + +// Death tests. +// Disabled on Android because death tests misbehave on Android, see +// base/test/gtest_util.h. +#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +TEST(RTCStatsDeathTest, ValueOfUndefinedMember) { + RTCTestStats stats("testId", 0.0); + EXPECT_FALSE(stats.m_int32.is_defined()); + EXPECT_DEATH(*stats.m_int32, ""); +} + +TEST(RTCStatsDeathTest, InvalidCasting) { + RTCGrandChildStats stats("grandchild", 0.0); + EXPECT_DEATH(stats.cast_to(), ""); +} + +#endif // RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +} // namespace webrtc diff --git a/webrtc/stats/rtcstatsreport.cc b/webrtc/stats/rtcstatsreport.cc new file mode 100644 index 0000000000..870651d2df --- /dev/null +++ b/webrtc/stats/rtcstatsreport.cc @@ -0,0 +1,86 @@ +/* + * Copyright 2016 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/api/rtcstatsreport.h" + +namespace webrtc { + +RTCStatsReport::ConstIterator::ConstIterator( + const rtc::scoped_refptr& report, + StatsMap::const_iterator it) + : report_(report), + it_(it) { +} + +RTCStatsReport::ConstIterator::ConstIterator(const ConstIterator&& other) + : report_(std::move(other.report_)), + it_(std::move(other.it_)) { +} + +RTCStatsReport::ConstIterator::~ConstIterator() { +} + +RTCStatsReport::ConstIterator& RTCStatsReport::ConstIterator::operator++() { + ++it_; + return *this; +} + +RTCStatsReport::ConstIterator& RTCStatsReport::ConstIterator::operator++(int) { + return ++(*this); +} + +const RTCStats& RTCStatsReport::ConstIterator::operator*() const { + return *it_->second.get(); +} + +bool RTCStatsReport::ConstIterator::operator==( + const RTCStatsReport::ConstIterator& other) const { + return it_ == other.it_; +} + +bool RTCStatsReport::ConstIterator::operator!=( + const RTCStatsReport::ConstIterator& other) const { + return !(*this == other); +} + +rtc::scoped_refptr RTCStatsReport::Create() { + return rtc::scoped_refptr( + new rtc::RefCountedObject()); +} + +RTCStatsReport::RTCStatsReport() { +} + +RTCStatsReport::~RTCStatsReport() { +} + +bool RTCStatsReport::AddStats(std::unique_ptr stats) { + return !stats_.insert(std::make_pair(std::string(stats->id()), + std::move(stats))).second; +} + +const RTCStats* RTCStatsReport::operator[](const std::string& id) const { + StatsMap::const_iterator it = stats_.find(id); + if (it != stats_.cend()) + return it->second.get(); + return nullptr; +} + +RTCStatsReport::ConstIterator RTCStatsReport::begin() const { + return ConstIterator(rtc::scoped_refptr(this), + stats_.cbegin()); +} + +RTCStatsReport::ConstIterator RTCStatsReport::end() const { + return ConstIterator(rtc::scoped_refptr(this), + stats_.cend()); +} + +} // namespace webrtc diff --git a/webrtc/stats/stats.gyp b/webrtc/stats/stats.gyp new file mode 100644 index 0000000000..9310aabe99 --- /dev/null +++ b/webrtc/stats/stats.gyp @@ -0,0 +1,45 @@ +# Copyright (c) 2016 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. + +{ + 'includes': [ '../build/common.gypi', ], + 'targets': [ + { + # GN version: webrtc/stats:rtc_stats + 'target_name': 'rtc_stats', + 'type': 'static_library', + 'dependencies': [ + '<(webrtc_root)/api/api.gyp:libjingle_peerconnection', + ], + 'sources': [ + 'rtcstats.cc', + 'rtcstatsreport.cc', + ], + }, + ], + 'conditions': [ + ['include_tests==1', { + 'targets': [ + { + # GN version: webrtc/stats:rtc_stats_unittests + 'target_name': 'rtc_stats_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(webrtc_root)/base/base_tests.gyp:rtc_base_tests_utils', + '<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default', + 'rtc_stats', + ], + 'sources': [ + 'rtcstats_unittest.cc', + ], + }, + ], + }], + ], +}