[Stats] Introduce Attribute, implementing RTCStatsMemberInterface.

The plan is to replace Members() with Attributes() instead.
For backwards-compatability during the transition, Attribute implements
RTCStatsMemberInterface but the two classes serve the same purpose
which is to allow iterating all metrics of a stats object.

The reason for moving away from "members" is that we already have a way
to express a variable that maybe has a value: absl::optional<T>. The
only information the member adds is the const char* name(), which we'll
move to Attribute in a future CL.

We don't need to maintain an RTCStatsMemberInterface::Type enum in the
future because absl::variant<T> has absl::holds_alternative<T>.

Step 1: Add Attributes().
Step 2: Migrate to Attributes() and delete Members().
Step 3: Replaces all uses of RTCStatsMember<T> with absl::optional<T>
        and delete RTCStatsMember + RTCStatsMemberInterface.

Bug: webrtc:15164
Change-Id: I3fdd5b24214bb5cc340a54a0171df73b516e1803
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/333840
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41507}
This commit is contained in:
Henrik Boström 2024-01-11 11:41:10 +01:00 committed by WebRTC LUCI CQ
parent 24b034c51b
commit 7978cf1b43
10 changed files with 298 additions and 53 deletions

View File

@ -771,6 +771,7 @@ rtc_source_set("rtc_stats_api") {
visibility = [ "*" ] visibility = [ "*" ]
cflags = [] cflags = []
sources = [ sources = [
"stats/attribute.h",
"stats/rtc_stats.h", "stats/rtc_stats.h",
"stats/rtc_stats_collector_callback.h", "stats/rtc_stats_collector_callback.h",
"stats/rtc_stats_member.h", "stats/rtc_stats_member.h",
@ -789,7 +790,10 @@ rtc_source_set("rtc_stats_api") {
"units:timestamp", "units:timestamp",
] ]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] absl_deps = [
"//third_party/abseil-cpp/absl/types:optional",
"//third_party/abseil-cpp/absl/types:variant",
]
} }
rtc_library("audio_options_api") { rtc_library("audio_options_api") {

79
api/stats/attribute.h Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright 2024 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 API_STATS_ATTRIBUTE_H_
#define API_STATS_ATTRIBUTE_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/types/variant.h"
#include "api/stats/rtc_stats_member.h"
#include "rtc_base/system/rtc_export.h"
namespace webrtc {
// A light-weight wrapper of an RTCStats attribute (an individual metric).
class RTC_EXPORT Attribute : public RTCStatsMemberInterface {
public:
// TODO(https://crbug.com/webrtc/15164): Replace uses of RTCStatsMember<T>
// with absl::optional<T> and update these pointer types.
typedef absl::variant<const RTCStatsMember<bool>*,
const RTCStatsMember<int32_t>*,
const RTCStatsMember<uint32_t>*,
const RTCStatsMember<int64_t>*,
const RTCStatsMember<uint64_t>*,
const RTCStatsMember<double>*,
const RTCStatsMember<std::string>*,
const RTCStatsMember<std::vector<bool>>*,
const RTCStatsMember<std::vector<int32_t>>*,
const RTCStatsMember<std::vector<uint32_t>>*,
const RTCStatsMember<std::vector<int64_t>>*,
const RTCStatsMember<std::vector<uint64_t>>*,
const RTCStatsMember<std::vector<double>>*,
const RTCStatsMember<std::vector<std::string>>*,
const RTCStatsMember<std::map<std::string, uint64_t>>*,
const RTCStatsMember<std::map<std::string, double>>*>
StatVariant;
template <typename T>
explicit Attribute(const RTCStatsMember<T>* attribute)
: RTCStatsMemberInterface(attribute->name()), attribute_(attribute) {}
~Attribute() override;
const char* name() const;
const StatVariant& as_variant() const;
static Attribute FromMemberInterface(const RTCStatsMemberInterface* member);
// RTCStatsMemberInterface implementation.
// TODO(https://crbug.com/webrtc/15164): Delete RTCStatsMemberInterface in
// favor of absl::optional<T>.
RTCStatsMemberInterface::Type type() const override;
bool is_sequence() const override;
bool is_string() const override;
bool is_defined() const override;
std::string ValueToString() const override;
std::string ValueToJson() const override;
protected:
const RTCStatsMemberInterface* member_ptr() const override;
bool IsEqual(const RTCStatsMemberInterface& other) const override;
private:
StatVariant attribute_;
};
Attribute MemberToAttribute(const RTCStatsMemberInterface* member);
} // namespace webrtc
#endif // API_STATS_ATTRIBUTE_H_

View File

@ -20,6 +20,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "api/stats/attribute.h"
#include "api/stats/rtc_stats_member.h" #include "api/stats/rtc_stats_member.h"
#include "api/units/timestamp.h" #include "api/units/timestamp.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
@ -56,8 +57,8 @@ class RTC_EXPORT RTCStats {
public: public:
RTCStats(const std::string& id, Timestamp timestamp) RTCStats(const std::string& id, Timestamp timestamp)
: id_(id), timestamp_(timestamp) {} : id_(id), timestamp_(timestamp) {}
RTCStats(const RTCStats& other);
virtual ~RTCStats() {} virtual ~RTCStats();
virtual std::unique_ptr<RTCStats> copy() const = 0; virtual std::unique_ptr<RTCStats> copy() const = 0;
@ -67,9 +68,12 @@ class RTC_EXPORT RTCStats {
// Returns the static member variable `kType` of the implementing class. // Returns the static member variable `kType` of the implementing class.
virtual const char* type() const = 0; virtual const char* type() const = 0;
// Returns a vector of pointers to all the `RTCStatsMemberInterface` members // Returns all attributes of this stats object, i.e. a list of its individual
// of this class. This allows for iteration of members. For a given class, // metrics as viewed via the Attribute wrapper.
// `Members` always returns the same members in the same order. std::vector<Attribute> Attributes() const;
// Returns Attributes() as `RTCStatsMemberInterface` pointers.
// TODO(https://crbug.com/webrtc/15164): Update callers to use Attributes()
// instead and delete this method as well as the RTCStatsMemberInterface.
std::vector<const RTCStatsMemberInterface*> Members() const; std::vector<const RTCStatsMemberInterface*> Members() const;
// Checks if the two stats objects are of the same type and have the same // Checks if the two stats objects are of the same type and have the same
// member values. Timestamps are not compared. These operators are exposed for // member values. Timestamps are not compared. These operators are exposed for
@ -90,15 +94,18 @@ class RTC_EXPORT RTCStats {
} }
protected: protected:
// Gets a vector of all members of this `RTCStats` object, including members virtual std::vector<Attribute> AttributesImpl(
// derived from parent classes. `additional_capacity` is how many more members size_t additional_capacity) const;
// 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<const RTCStatsMemberInterface*>
MembersOfThisObjectAndAncestors(size_t additional_capacity) const;
std::string const id_; std::string const id_;
Timestamp timestamp_; Timestamp timestamp_;
// Because Members() return a raw pointers we need to cache attributes to
// ensure the pointers are still valid after the method has returned. Mutable
// to allow lazy instantiation the first time the method is called.
// TODO(https://crbug.com/webrtc/15164): Migrate all uses of Members() to
// Attributes() and delete Members() and `cached_attributes_`.
mutable std::vector<Attribute> cached_attributes_;
}; };
// All `RTCStats` classes should use these macros. // All `RTCStats` classes should use these macros.
@ -142,16 +149,15 @@ class RTC_EXPORT RTCStats {
// bar("bar") { // bar("bar") {
// } // }
// //
#define WEBRTC_RTCSTATS_DECL() \ #define WEBRTC_RTCSTATS_DECL() \
protected: \ protected: \
std::vector<const webrtc::RTCStatsMemberInterface*> \ std::vector<webrtc::Attribute> AttributesImpl(size_t additional_capacity) \
MembersOfThisObjectAndAncestors(size_t local_var_additional_capacity) \ const override; \
const override; \ \
\ public: \
public: \ static const char kType[]; \
static const char kType[]; \ \
\ std::unique_ptr<webrtc::RTCStats> copy() const override; \
std::unique_ptr<webrtc::RTCStats> copy() const override; \
const char* type() const override const char* type() const override
#define WEBRTC_RTCSTATS_IMPL(this_class, parent_class, type_str, ...) \ #define WEBRTC_RTCSTATS_IMPL(this_class, parent_class, type_str, ...) \
@ -165,23 +171,17 @@ class RTC_EXPORT RTCStats {
return this_class::kType; \ return this_class::kType; \
} \ } \
\ \
std::vector<const webrtc::RTCStatsMemberInterface*> \ std::vector<webrtc::Attribute> this_class::AttributesImpl( \
this_class::MembersOfThisObjectAndAncestors( \ size_t additional_capacity) const { \
size_t local_var_additional_capacity) const { \ const webrtc::RTCStatsMemberInterface* this_members[] = {__VA_ARGS__}; \
const webrtc::RTCStatsMemberInterface* local_var_members[] = { \ size_t this_members_size = sizeof(this_members) / sizeof(this_members[0]); \
__VA_ARGS__}; \ std::vector<webrtc::Attribute> attributes = \
size_t local_var_members_count = \ parent_class::AttributesImpl(this_members_size + additional_capacity); \
sizeof(local_var_members) / sizeof(local_var_members[0]); \ for (size_t i = 0; i < this_members_size; ++i) { \
std::vector<const webrtc::RTCStatsMemberInterface*> \ attributes.push_back( \
local_var_members_vec = parent_class::MembersOfThisObjectAndAncestors( \ webrtc::Attribute::FromMemberInterface(this_members[i])); \
local_var_members_count + local_var_additional_capacity); \ } \
RTC_DCHECK_GE( \ return attributes; \
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; \
} }
// A version of WEBRTC_RTCSTATS_IMPL() where "..." is omitted, used to avoid a // A version of WEBRTC_RTCSTATS_IMPL() where "..." is omitted, used to avoid a
@ -198,10 +198,9 @@ class RTC_EXPORT RTCStats {
return this_class::kType; \ return this_class::kType; \
} \ } \
\ \
std::vector<const webrtc::RTCStatsMemberInterface*> \ std::vector<webrtc::Attribute> this_class::AttributesImpl( \
this_class::MembersOfThisObjectAndAncestors( \ size_t additional_capacity) const { \
size_t local_var_additional_capacity) const { \ return parent_class::AttributesImpl(0); \
return parent_class::MembersOfThisObjectAndAncestors(0); \
} }
} // namespace webrtc } // namespace webrtc

View File

@ -74,10 +74,11 @@ class RTCStatsMemberInterface {
// instead. // instead.
virtual std::string ValueToJson() const = 0; virtual std::string ValueToJson() const = 0;
virtual const RTCStatsMemberInterface* member_ptr() const { return this; }
template <typename T> template <typename T>
const T& cast_to() const { const T& cast_to() const {
RTC_DCHECK_EQ(type(), T::StaticType()); RTC_DCHECK_EQ(type(), T::StaticType());
return static_cast<const T&>(*this); return static_cast<const T&>(*member_ptr());
} }
protected: protected:
@ -151,7 +152,6 @@ class RTCStatsMember : public RTCStatsMemberInterface {
return &(*value_); return &(*value_);
} }
protected:
bool IsEqual(const RTCStatsMemberInterface& other) const override { bool IsEqual(const RTCStatsMemberInterface& other) const override {
if (type() != other.type()) if (type() != other.type())
return false; return false;

View File

@ -29,6 +29,7 @@
#include "api/media_stream_track.h" #include "api/media_stream_track.h"
#include "api/rtp_parameters.h" #include "api/rtp_parameters.h"
#include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_direction.h"
#include "api/stats/attribute.h"
#include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats.h"
#include "api/stats/rtc_stats_report.h" #include "api/stats/rtc_stats_report.h"
#include "api/stats/rtcstats_objects.h" #include "api/stats/rtcstats_objects.h"

View File

@ -207,13 +207,13 @@ class RTCStatsVerifier {
RTC_CHECK(report_); RTC_CHECK(report_);
RTC_CHECK(stats_); RTC_CHECK(stats_);
for (const RTCStatsMemberInterface* member : stats_->Members()) { for (const RTCStatsMemberInterface* member : stats_->Members()) {
untested_members_.insert(member); untested_members_.insert(member->member_ptr());
} }
} }
void MarkMemberTested(const RTCStatsMemberInterface& member, void MarkMemberTested(const RTCStatsMemberInterface& member,
bool test_successful) { bool test_successful) {
untested_members_.erase(&member); untested_members_.erase(member.member_ptr());
all_tests_successful_ &= test_successful; all_tests_successful_ &= test_successful;
} }

View File

@ -16,6 +16,7 @@ rtc_library("rtc_stats") {
visibility = [ "*" ] visibility = [ "*" ]
cflags = [] cflags = []
sources = [ sources = [
"attribute.cc",
"rtc_stats.cc", "rtc_stats.cc",
"rtc_stats_member.cc", "rtc_stats_member.cc",
"rtc_stats_report.cc", "rtc_stats_report.cc",
@ -28,6 +29,7 @@ rtc_library("rtc_stats") {
"../rtc_base:macromagic", "../rtc_base:macromagic",
"../rtc_base:stringutils", "../rtc_base:stringutils",
] ]
absl_deps = [ "//third_party/abseil-cpp/absl/types:variant" ]
} }
rtc_library("rtc_stats_test_utils") { rtc_library("rtc_stats_test_utils") {

142
stats/attribute.cc Normal file
View File

@ -0,0 +1,142 @@
/*
* Copyright 2024 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 "api/stats/attribute.h"
#include "absl/types/variant.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
struct VisitIsSequence {
// Any type of vector is a sequence.
template <typename T>
bool operator()(const RTCStatsMember<std::vector<T>>* attribute) {
return true;
}
// Any other type is not.
template <typename T>
bool operator()(const RTCStatsMember<T>* attribute) {
return false;
}
};
struct VisitIsEqual {
template <typename T>
bool operator()(const RTCStatsMember<T>* attribute) {
return attribute->IsEqual(other);
}
const RTCStatsMemberInterface& other;
};
} // namespace
Attribute::~Attribute() {}
// static
Attribute Attribute::FromMemberInterface(
const RTCStatsMemberInterface* member) {
switch (member->type()) {
case RTCStatsMemberInterface::Type::kBool:
return Attribute(&member->cast_to<RTCStatsMember<bool>>());
case RTCStatsMemberInterface::Type::kInt32:
return Attribute(&member->cast_to<RTCStatsMember<int32_t>>());
case RTCStatsMemberInterface::Type::kUint32:
return Attribute(&member->cast_to<RTCStatsMember<uint32_t>>());
case RTCStatsMemberInterface::Type::kInt64:
return Attribute(&member->cast_to<RTCStatsMember<int64_t>>());
case RTCStatsMemberInterface::Type::kUint64:
return Attribute(&member->cast_to<RTCStatsMember<uint64_t>>());
case RTCStatsMemberInterface::Type::kDouble:
return Attribute(&member->cast_to<RTCStatsMember<double>>());
case RTCStatsMemberInterface::Type::kString:
return Attribute(&member->cast_to<RTCStatsMember<std::string>>());
case RTCStatsMemberInterface::Type::kSequenceBool:
return Attribute(&member->cast_to<RTCStatsMember<std::vector<bool>>>());
case RTCStatsMemberInterface::Type::kSequenceInt32:
return Attribute(
&member->cast_to<RTCStatsMember<std::vector<int32_t>>>());
case RTCStatsMemberInterface::Type::kSequenceUint32:
return Attribute(
&member->cast_to<RTCStatsMember<std::vector<uint32_t>>>());
case RTCStatsMemberInterface::Type::kSequenceInt64:
return Attribute(
&member->cast_to<RTCStatsMember<std::vector<int64_t>>>());
case RTCStatsMemberInterface::Type::kSequenceUint64:
return Attribute(
&member->cast_to<RTCStatsMember<std::vector<uint64_t>>>());
case RTCStatsMemberInterface::Type::kSequenceDouble:
return Attribute(&member->cast_to<RTCStatsMember<std::vector<double>>>());
case RTCStatsMemberInterface::Type::kSequenceString:
return Attribute(
&member->cast_to<RTCStatsMember<std::vector<std::string>>>());
case RTCStatsMemberInterface::Type::kMapStringUint64:
return Attribute(
&member->cast_to<RTCStatsMember<std::map<std::string, uint64_t>>>());
case RTCStatsMemberInterface::Type::kMapStringDouble:
return Attribute(
&member->cast_to<RTCStatsMember<std::map<std::string, double>>>());
default:
RTC_CHECK_NOTREACHED();
}
}
const char* Attribute::name() const {
return absl::visit([](const auto* attr) { return attr->name(); }, attribute_);
}
const Attribute::StatVariant& Attribute::as_variant() const {
return attribute_;
}
RTCStatsMemberInterface::Type Attribute::type() const {
return absl::visit([](const auto* attr) { return attr->type(); }, attribute_);
}
const RTCStatsMemberInterface* Attribute::member_ptr() const {
return absl::visit(
[](const auto* attr) {
return static_cast<const RTCStatsMemberInterface*>(attr);
},
attribute_);
}
bool Attribute::is_sequence() const {
return absl::visit(VisitIsSequence(), attribute_);
}
bool Attribute::is_string() const {
return absl::holds_alternative<const RTCStatsMember<std::string>*>(
attribute_);
}
bool Attribute::is_defined() const {
return absl::visit([](const auto* attr) { return attr->is_defined(); },
attribute_);
}
std::string Attribute::ValueToString() const {
return absl::visit([](const auto* attr) { return attr->ValueToString(); },
attribute_);
}
std::string Attribute::ValueToJson() const {
return absl::visit([](const auto* attr) { return attr->ValueToJson(); },
attribute_);
}
bool Attribute::IsEqual(const RTCStatsMemberInterface& other) const {
return absl::visit(VisitIsEqual{.other = *other.member_ptr()}, attribute_);
}
} // namespace webrtc

View File

@ -16,6 +16,11 @@
namespace webrtc { namespace webrtc {
RTCStats::RTCStats(const RTCStats& other)
: RTCStats(other.id_, other.timestamp_) {}
RTCStats::~RTCStats() {}
bool RTCStats::operator==(const RTCStats& other) const { bool RTCStats::operator==(const RTCStats& other) const {
if (type() != other.type() || id() != other.id()) if (type() != other.type() || id() != other.id())
return false; return false;
@ -60,14 +65,26 @@ std::string RTCStats::ToJson() const {
} }
std::vector<const RTCStatsMemberInterface*> RTCStats::Members() const { std::vector<const RTCStatsMemberInterface*> RTCStats::Members() const {
return MembersOfThisObjectAndAncestors(0); if (cached_attributes_.empty()) {
} cached_attributes_ = Attributes();
}
std::vector<const RTCStatsMemberInterface*>
RTCStats::MembersOfThisObjectAndAncestors(size_t additional_capacity) const {
std::vector<const RTCStatsMemberInterface*> members; std::vector<const RTCStatsMemberInterface*> members;
members.reserve(additional_capacity); members.reserve(cached_attributes_.size());
for (const auto& attribute : cached_attributes_) {
members.push_back(&attribute);
}
return members; return members;
} }
std::vector<Attribute> RTCStats::Attributes() const {
return AttributesImpl(0);
}
std::vector<Attribute> RTCStats::AttributesImpl(
size_t additional_capacity) const {
std::vector<Attribute> attributes;
attributes.reserve(additional_capacity);
return attributes;
}
} // namespace webrtc } // namespace webrtc

View File

@ -12,6 +12,7 @@
#include <utility> #include <utility>
#include "api/stats/attribute.h"
#include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"