diff --git a/api/numerics/samples_stats_counter.h b/api/numerics/samples_stats_counter.h index 9387e6be9b..283c1e4ed2 100644 --- a/api/numerics/samples_stats_counter.h +++ b/api/numerics/samples_stats_counter.h @@ -45,6 +45,8 @@ class SamplesStatsCounter { // Returns if there are any values in O(1) time. bool IsEmpty() const { return samples_.empty(); } + // Returns the amount of samples added into counter in O(1) time. + int64_t NumSamples() const { return stats_.Size(); } // Returns min in O(1) time. This function may not be called if there are no // samples. diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn index 4780da2451..fb7bedc003 100644 --- a/api/test/network_emulation/BUILD.gn +++ b/api/test/network_emulation/BUILD.gn @@ -21,6 +21,7 @@ rtc_library("network_emulation") { "../../../rtc_base", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", + "../../numerics", "../../units:data_rate", "../../units:data_size", "../../units:timestamp", diff --git a/api/test/network_emulation/network_emulation_interfaces.h b/api/test/network_emulation/network_emulation_interfaces.h index 7c1a61f2f6..36fb996549 100644 --- a/api/test/network_emulation/network_emulation_interfaces.h +++ b/api/test/network_emulation/network_emulation_interfaces.h @@ -16,6 +16,7 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/numerics/samples_stats_counter.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/timestamp.h" @@ -69,6 +70,12 @@ class EmulatedNetworkOutgoingStats { virtual DataSize BytesSent() const = 0; + // Returns the timestamped sizes of all sent packets if + // EmulatedEndpointConfig::stats_gatherming_mode was set to + // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // Returned reference is valid until the next call to a non-const method. + virtual const SamplesStatsCounter& SentPacketsSizeCounter() const = 0; + virtual DataSize FirstSentPacketSize() const = 0; // Returns time of the first packet sent or infinite value if no packets were @@ -91,10 +98,21 @@ class EmulatedNetworkIncomingStats { virtual int64_t PacketsReceived() const = 0; // Total amount of bytes in received packets. virtual DataSize BytesReceived() const = 0; + // Returns the timestamped sizes of all received packets if + // EmulatedEndpointConfig::stats_gatherming_mode was set to + // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // Returned reference is valid until the next call to a non-const method. + virtual const SamplesStatsCounter& ReceivedPacketsSizeCounter() const = 0; // Total amount of packets that were received, but no destination was found. virtual int64_t PacketsDropped() const = 0; // Total amount of bytes in dropped packets. virtual DataSize BytesDropped() const = 0; + // Returns the timestamped sizes of all packets that were received, + // but no destination was found if + // EmulatedEndpointConfig::stats_gatherming_mode was set to + // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // Returned reference is valid until the next call to a non-const method. + virtual const SamplesStatsCounter& DroppedPacketsSizeCounter() const = 0; virtual DataSize FirstReceivedPacketSize() const = 0; @@ -120,6 +138,17 @@ class EmulatedNetworkStats { virtual int64_t PacketsSent() const = 0; virtual DataSize BytesSent() const = 0; + // Returns the timestamped sizes of all sent packets if + // EmulatedEndpointConfig::stats_gatherming_mode was set to + // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // Returned reference is valid until the next call to a non-const method. + virtual const SamplesStatsCounter& SentPacketsSizeCounter() const = 0; + // Returns the timestamped duration between packet was received on + // network interface and was dispatched to the network in microseconds if + // EmulatedEndpointConfig::stats_gatherming_mode was set to + // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // Returned reference is valid until the next call to a non-const method. + virtual const SamplesStatsCounter& SentPacketsQueueWaitTimeUs() const = 0; virtual DataSize FirstSentPacketSize() const = 0; // Returns time of the first packet sent or infinite value if no packets were @@ -134,10 +163,21 @@ class EmulatedNetworkStats { virtual int64_t PacketsReceived() const = 0; // Total amount of bytes in received packets. virtual DataSize BytesReceived() const = 0; + // Returns the timestamped sizes of all received packets if + // EmulatedEndpointConfig::stats_gatherming_mode was set to + // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // Returned reference is valid until the next call to a non-const method. + virtual const SamplesStatsCounter& ReceivedPacketsSizeCounter() const = 0; // Total amount of packets that were received, but no destination was found. virtual int64_t PacketsDropped() const = 0; // Total amount of bytes in dropped packets. virtual DataSize BytesDropped() const = 0; + // Returns counter with timestamped sizes of all packets that were received, + // but no destination was found if + // EmulatedEndpointConfig::stats_gatherming_mode was set to + // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // Returned reference is valid until the next call to a non-const method. + virtual const SamplesStatsCounter& DroppedPacketsSizeCounter() const = 0; virtual DataSize FirstReceivedPacketSize() const = 0; // Returns time of the first packet received or infinite value if no packets diff --git a/api/test/network_emulation_manager.h b/api/test/network_emulation_manager.h index d2e6417a9d..58ee3bfd1a 100644 --- a/api/test/network_emulation_manager.h +++ b/api/test/network_emulation_manager.h @@ -46,6 +46,13 @@ class EmulatedRoute; struct EmulatedEndpointConfig { enum class IpAddressFamily { kIpv4, kIpv6 }; + enum class StatsGatheringMode { + // Gather main network stats counters. + kDefault, + // kDefault + also gather per packet statistics. In this mode more memory + // will be used. + kDebug + }; IpAddressFamily generated_ip_family = IpAddressFamily::kIpv4; // If specified will be used as IP address for endpoint node. Must be unique @@ -56,6 +63,7 @@ struct EmulatedEndpointConfig { bool start_as_enabled = true; // Network type which will be used to represent endpoint to WebRTC. rtc::AdapterType type = rtc::AdapterType::ADAPTER_TYPE_UNKNOWN; + StatsGatheringMode stats_gathering_mode = StatsGatheringMode::kDefault; }; diff --git a/test/network/BUILD.gn b/test/network/BUILD.gn index 71d0533546..a632849f83 100644 --- a/test/network/BUILD.gn +++ b/test/network/BUILD.gn @@ -10,13 +10,13 @@ import("../../webrtc.gni") rtc_library("emulated_network") { visibility = [ - "../../api:create_network_emulation_manager", ":*", + "../../api:create_network_emulation_manager", ] if (rtc_include_tests) { visibility += [ - "../scenario:*", "../peer_scenario:*", + "../scenario:*", ] } testonly = true @@ -39,6 +39,7 @@ rtc_library("emulated_network") { "../../api:network_emulation_manager_api", "../../api:simulated_network_api", "../../api:time_controller", + "../../api/numerics", "../../api/units:data_rate", "../../api/units:data_size", "../../api/units:time_delta", @@ -114,6 +115,7 @@ rtc_library("cross_traffic_unittest") { deps = [ ":emulated_network", "../:test_support", + "../../api:network_emulation_manager_api", "../../api:simulated_network_api", "../../call:simulated_network", "../../rtc_base", diff --git a/test/network/cross_traffic_unittest.cc b/test/network/cross_traffic_unittest.cc index a3c7b42311..c8d848f154 100644 --- a/test/network/cross_traffic_unittest.cc +++ b/test/network/cross_traffic_unittest.cc @@ -16,6 +16,7 @@ #include #include "absl/memory/memory.h" +#include "api/test/network_emulation_manager.h" #include "api/test/simulated_network.h" #include "call/simulated_network.h" #include "rtc_base/event.h" @@ -46,12 +47,14 @@ struct TrafficCounterFixture { SimulatedClock clock{0}; CountingReceiver counter; TaskQueueForTest task_queue_; - EmulatedEndpointImpl endpoint{/*id=*/1, - rtc::IPAddress(kTestIpAddress), - /*is_enabled=*/true, - /*type=*/rtc::AdapterType::ADAPTER_TYPE_UNKNOWN, - &task_queue_, - &clock}; + EmulatedEndpointImpl endpoint{ + /*id=*/1, + rtc::IPAddress(kTestIpAddress), + EmulatedEndpointConfig::StatsGatheringMode::kDefault, + /*is_enabled=*/true, + /*type=*/rtc::AdapterType::ADAPTER_TYPE_UNKNOWN, + &task_queue_, + &clock}; }; } // namespace diff --git a/test/network/network_emulation.cc b/test/network/network_emulation.cc index 13d3d87349..b969dfce38 100644 --- a/test/network/network_emulation.cc +++ b/test/network/network_emulation.cc @@ -14,6 +14,7 @@ #include #include +#include "api/numerics/samples_stats_counter.h" #include "api/units/data_size.h" #include "rtc_base/bind.h" #include "rtc_base/logging.h" @@ -78,8 +79,10 @@ EmulatedNetworkOutgoingStatsBuilder::EmulatedNetworkOutgoingStatsBuilder() { sequence_checker_.Detach(); } -void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(Timestamp sent_time, - DataSize packet_size) { +void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent( + Timestamp sent_time, + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode) { RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_CHECK_GE(packet_size, DataSize::Zero()); if (first_packet_sent_time_.IsInfinite()) { @@ -89,6 +92,9 @@ void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(Timestamp sent_time, last_packet_sent_time_ = sent_time; packets_sent_++; bytes_sent_ += packet_size; + if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) { + sent_packets_size_counter_.AddSample(packet_size.bytes()); + } } void EmulatedNetworkOutgoingStatsBuilder::AddOutgoingStats( @@ -96,6 +102,7 @@ void EmulatedNetworkOutgoingStatsBuilder::AddOutgoingStats( RTC_DCHECK_RUN_ON(&sequence_checker_); packets_sent_ += stats.PacketsSent(); bytes_sent_ += stats.BytesSent(); + sent_packets_size_counter_.AddSamples(stats.SentPacketsSizeCounter()); if (first_packet_sent_time_ > stats.FirstPacketSentTime()) { first_packet_sent_time_ = stats.FirstPacketSentTime(); first_sent_packet_size_ = stats.FirstSentPacketSize(); @@ -109,8 +116,8 @@ std::unique_ptr EmulatedNetworkOutgoingStatsBuilder::Build() const { RTC_DCHECK_RUN_ON(&sequence_checker_); return std::make_unique( - packets_sent_, bytes_sent_, first_sent_packet_size_, - first_packet_sent_time_, last_packet_sent_time_); + packets_sent_, bytes_sent_, sent_packets_size_counter_, + first_sent_packet_size_, first_packet_sent_time_, last_packet_sent_time_); } EmulatedNetworkIncomingStatsBuilder::EmulatedNetworkIncomingStatsBuilder() { @@ -118,15 +125,20 @@ EmulatedNetworkIncomingStatsBuilder::EmulatedNetworkIncomingStatsBuilder() { } void EmulatedNetworkIncomingStatsBuilder::OnPacketDropped( - DataSize packet_size) { + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode) { RTC_DCHECK_RUN_ON(&sequence_checker_); packets_dropped_++; bytes_dropped_ += packet_size; + if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) { + dropped_packets_size_counter_.AddSample(packet_size.bytes()); + } } void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived( Timestamp received_time, - DataSize packet_size) { + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode) { RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_CHECK_GE(packet_size, DataSize::Zero()); if (first_packet_received_time_.IsInfinite()) { @@ -136,6 +148,9 @@ void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived( last_packet_received_time_ = received_time; packets_received_++; bytes_received_ += packet_size; + if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) { + received_packets_size_counter_.AddSample(packet_size.bytes()); + } } void EmulatedNetworkIncomingStatsBuilder::AddIncomingStats( @@ -143,8 +158,10 @@ void EmulatedNetworkIncomingStatsBuilder::AddIncomingStats( RTC_DCHECK_RUN_ON(&sequence_checker_); packets_received_ += stats.PacketsReceived(); bytes_received_ += stats.BytesReceived(); + received_packets_size_counter_.AddSamples(stats.ReceivedPacketsSizeCounter()); packets_dropped_ += stats.PacketsDropped(); bytes_dropped_ += stats.BytesDropped(); + dropped_packets_size_counter_.AddSamples(stats.DroppedPacketsSizeCounter()); if (first_packet_received_time_ > stats.FirstPacketReceivedTime()) { first_packet_received_time_ = stats.FirstPacketReceivedTime(); first_received_packet_size_ = stats.FirstReceivedPacketSize(); @@ -158,7 +175,8 @@ std::unique_ptr EmulatedNetworkIncomingStatsBuilder::Build() const { RTC_DCHECK_RUN_ON(&sequence_checker_); return std::make_unique( - packets_received_, bytes_received_, packets_dropped_, bytes_dropped_, + packets_received_, bytes_received_, received_packets_size_counter_, + packets_dropped_, bytes_dropped_, dropped_packets_size_counter_, first_received_packet_size_, first_packet_received_time_, last_packet_received_time_); } @@ -173,26 +191,36 @@ EmulatedNetworkStatsBuilder::EmulatedNetworkStatsBuilder( sequence_checker_.Detach(); } -void EmulatedNetworkStatsBuilder::OnPacketSent(Timestamp sent_time, - rtc::IPAddress destination_ip, - DataSize packet_size) { +void EmulatedNetworkStatsBuilder::OnPacketSent( + Timestamp queued_time, + Timestamp sent_time, + rtc::IPAddress destination_ip, + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode) { RTC_DCHECK_RUN_ON(&sequence_checker_); - outgoing_stats_per_destination_[destination_ip].OnPacketSent(sent_time, - packet_size); + if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) { + sent_packets_queue_wait_time_us_.AddSample((sent_time - queued_time).us()); + } + outgoing_stats_per_destination_[destination_ip].OnPacketSent( + sent_time, packet_size, mode); } -void EmulatedNetworkStatsBuilder::OnPacketDropped(rtc::IPAddress source_ip, - DataSize packet_size) { +void EmulatedNetworkStatsBuilder::OnPacketDropped( + rtc::IPAddress source_ip, + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode) { RTC_DCHECK_RUN_ON(&sequence_checker_); - incoming_stats_per_source_[source_ip].OnPacketDropped(packet_size); + incoming_stats_per_source_[source_ip].OnPacketDropped(packet_size, mode); } -void EmulatedNetworkStatsBuilder::OnPacketReceived(Timestamp received_time, - rtc::IPAddress source_ip, - DataSize packet_size) { +void EmulatedNetworkStatsBuilder::OnPacketReceived( + Timestamp received_time, + rtc::IPAddress source_ip, + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode) { RTC_DCHECK_RUN_ON(&sequence_checker_); incoming_stats_per_source_[source_ip].OnPacketReceived(received_time, - packet_size); + packet_size, mode); } void EmulatedNetworkStatsBuilder::AddEmulatedNetworkStats( @@ -204,6 +232,9 @@ void EmulatedNetworkStatsBuilder::AddEmulatedNetworkStats( local_addresses_.push_back(addr); } + sent_packets_queue_wait_time_us_.AddSamples( + stats.SentPacketsQueueWaitTimeUs()); + // Add outgoing stats from other endpoints to the builder. const std::map> outgoing_stats_per_destination = stats.OutgoingStatsPerDestination(); @@ -234,7 +265,8 @@ std::unique_ptr EmulatedNetworkStatsBuilder::Build() incoming_stats.emplace(entry.first, entry.second.Build()); } return std::make_unique( - local_addresses_, std::move(outgoing_stats), std::move(incoming_stats)); + local_addresses_, sent_packets_queue_wait_time_us_, + std::move(outgoing_stats), std::move(incoming_stats)); } void LinkEmulation::OnPacketReceived(EmulatedIpPacket packet) { @@ -383,14 +415,17 @@ void EmulatedNetworkNode::ClearRoute(const rtc::IPAddress& receiver_ip, EmulatedNetworkNode::~EmulatedNetworkNode() = default; -EmulatedEndpointImpl::EmulatedEndpointImpl(uint64_t id, - const rtc::IPAddress& ip, - bool is_enabled, - rtc::AdapterType type, - rtc::TaskQueue* task_queue, - Clock* clock) +EmulatedEndpointImpl::EmulatedEndpointImpl( + uint64_t id, + const rtc::IPAddress& ip, + EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode, + bool is_enabled, + rtc::AdapterType type, + rtc::TaskQueue* task_queue, + Clock* clock) : id_(id), peer_local_addr_(ip), + stats_gathering_mode_(stats_gathering_mode), is_enabled_(is_enabled), type_(type), clock_(clock), @@ -430,8 +465,9 @@ void EmulatedEndpointImpl::SendPacket(const rtc::SocketAddress& from, clock_->CurrentTime(), application_overhead); task_queue_->PostTask([this, packet = std::move(packet)]() mutable { RTC_DCHECK_RUN_ON(task_queue_); - stats_builder_.OnPacketSent(clock_->CurrentTime(), packet.to.ipaddr(), - DataSize::Bytes(packet.ip_packet_size())); + stats_builder_.OnPacketSent( + packet.arrival_time, clock_->CurrentTime(), packet.to.ipaddr(), + DataSize::Bytes(packet.ip_packet_size()), stats_gathering_mode_); router_.OnPacketReceived(std::move(packet)); }); @@ -495,7 +531,8 @@ void EmulatedEndpointImpl::OnPacketReceived(EmulatedIpPacket packet) { << "; Receiver peer_local_addr_=" << peer_local_addr_.ToString(); rtc::CritScope crit(&receiver_lock_); stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet.from.ipaddr(), - DataSize::Bytes(packet.ip_packet_size())); + DataSize::Bytes(packet.ip_packet_size()), + stats_gathering_mode_); auto it = port_to_receiver_.find(packet.to.port()); if (it == port_to_receiver_.end()) { // It can happen, that remote peer closed connection, but there still some @@ -504,7 +541,8 @@ void EmulatedEndpointImpl::OnPacketReceived(EmulatedIpPacket packet) { RTC_LOG(INFO) << "Drop packet: no receiver registered in " << id_ << " on port " << packet.to.port(); stats_builder_.OnPacketDropped(packet.from.ipaddr(), - DataSize::Bytes(packet.ip_packet_size())); + DataSize::Bytes(packet.ip_packet_size()), + stats_gathering_mode_); return; } // Endpoint assumes frequent calls to bind and unbind methods, so it holds diff --git a/test/network/network_emulation.h b/test/network/network_emulation.h index d29253320d..13d4386d0d 100644 --- a/test/network/network_emulation.h +++ b/test/network/network_emulation.h @@ -21,6 +21,7 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/numerics/samples_stats_counter.h" #include "api/test/network_emulation_manager.h" #include "api/test/simulated_network.h" #include "api/units/timestamp.h" @@ -31,6 +32,7 @@ #include "rtc_base/synchronization/sequence_checker.h" #include "rtc_base/task_queue_for_test.h" #include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/thread_annotations.h" #include "rtc_base/thread_checker.h" #include "system_wrappers/include/clock.h" @@ -40,13 +42,16 @@ namespace webrtc { class EmulatedNetworkOutgoingStatsImpl final : public EmulatedNetworkOutgoingStats { public: - EmulatedNetworkOutgoingStatsImpl(int64_t packets_sent, - DataSize bytes_sent, - DataSize first_sent_packet_size, - Timestamp first_packet_sent_time, - Timestamp last_packet_sent_time) + EmulatedNetworkOutgoingStatsImpl( + int64_t packets_sent, + DataSize bytes_sent, + SamplesStatsCounter sent_packets_size_counter, + DataSize first_sent_packet_size, + Timestamp first_packet_sent_time, + Timestamp last_packet_sent_time) : packets_sent_(packets_sent), bytes_sent_(bytes_sent), + sent_packets_size_counter_(std::move(sent_packets_size_counter)), first_sent_packet_size_(first_sent_packet_size), first_packet_sent_time_(first_packet_sent_time), last_packet_sent_time_(last_packet_sent_time) {} @@ -54,6 +59,7 @@ class EmulatedNetworkOutgoingStatsImpl final const EmulatedNetworkOutgoingStats& stats) : packets_sent_(stats.PacketsSent()), bytes_sent_(stats.BytesSent()), + sent_packets_size_counter_(stats.SentPacketsSizeCounter()), first_sent_packet_size_(stats.FirstSentPacketSize()), first_packet_sent_time_(stats.FirstPacketSentTime()), last_packet_sent_time_(stats.LastPacketSentTime()) {} @@ -63,6 +69,10 @@ class EmulatedNetworkOutgoingStatsImpl final DataSize BytesSent() const override { return bytes_sent_; } + const SamplesStatsCounter& SentPacketsSizeCounter() const override { + return sent_packets_size_counter_; + } + DataSize FirstSentPacketSize() const override { return first_sent_packet_size_; } @@ -80,6 +90,7 @@ class EmulatedNetworkOutgoingStatsImpl final private: const int64_t packets_sent_; const DataSize bytes_sent_; + const SamplesStatsCounter sent_packets_size_counter_; const DataSize first_sent_packet_size_; const Timestamp first_packet_sent_time_; const Timestamp last_packet_sent_time_; @@ -89,17 +100,22 @@ class EmulatedNetworkOutgoingStatsImpl final class EmulatedNetworkIncomingStatsImpl final : public EmulatedNetworkIncomingStats { public: - EmulatedNetworkIncomingStatsImpl(int64_t packets_received, - DataSize bytes_received, - int64_t packets_dropped, - DataSize bytes_dropped, - DataSize first_received_packet_size, - Timestamp first_packet_received_time, - Timestamp last_packet_received_time) + EmulatedNetworkIncomingStatsImpl( + int64_t packets_received, + DataSize bytes_received, + SamplesStatsCounter received_packets_size_counter, + int64_t packets_dropped, + DataSize bytes_dropped, + SamplesStatsCounter dropped_packets_size_counter, + DataSize first_received_packet_size, + Timestamp first_packet_received_time, + Timestamp last_packet_received_time) : packets_received_(packets_received), bytes_received_(bytes_received), + received_packets_size_counter_(received_packets_size_counter), packets_dropped_(packets_dropped), bytes_dropped_(bytes_dropped), + dropped_packets_size_counter_(dropped_packets_size_counter), first_received_packet_size_(first_received_packet_size), first_packet_received_time_(first_packet_received_time), last_packet_received_time_(last_packet_received_time) {} @@ -107,8 +123,10 @@ class EmulatedNetworkIncomingStatsImpl final const EmulatedNetworkIncomingStats& stats) : packets_received_(stats.PacketsReceived()), bytes_received_(stats.BytesReceived()), + received_packets_size_counter_(stats.ReceivedPacketsSizeCounter()), packets_dropped_(stats.PacketsDropped()), bytes_dropped_(stats.BytesDropped()), + dropped_packets_size_counter_(stats.DroppedPacketsSizeCounter()), first_received_packet_size_(stats.FirstReceivedPacketSize()), first_packet_received_time_(stats.FirstPacketReceivedTime()), last_packet_received_time_(stats.LastPacketReceivedTime()) {} @@ -118,10 +136,18 @@ class EmulatedNetworkIncomingStatsImpl final DataSize BytesReceived() const override { return bytes_received_; } + const SamplesStatsCounter& ReceivedPacketsSizeCounter() const override { + return received_packets_size_counter_; + } + int64_t PacketsDropped() const override { return packets_dropped_; } DataSize BytesDropped() const override { return bytes_dropped_; } + const SamplesStatsCounter& DroppedPacketsSizeCounter() const override { + return dropped_packets_size_counter_; + } + DataSize FirstReceivedPacketSize() const override { return first_received_packet_size_; } @@ -139,8 +165,10 @@ class EmulatedNetworkIncomingStatsImpl final private: const int64_t packets_received_; const DataSize bytes_received_; + const SamplesStatsCounter received_packets_size_counter_; const int64_t packets_dropped_; const DataSize bytes_dropped_; + const SamplesStatsCounter dropped_packets_size_counter_; const DataSize first_received_packet_size_; const Timestamp first_packet_received_time_; const Timestamp last_packet_received_time_; @@ -151,14 +179,18 @@ class EmulatedNetworkStatsImpl final : public EmulatedNetworkStats { public: EmulatedNetworkStatsImpl( std::vector local_addresses, + SamplesStatsCounter sent_packets_queue_wait_time_us, std::map> outgoing_stats_per_destination, std::map> incoming_stats_per_source) : local_addresses_(std::move(local_addresses)), + sent_packets_queue_wait_time_us_(sent_packets_queue_wait_time_us), outgoing_stats_per_destination_( std::move(outgoing_stats_per_destination)), - incoming_stats_per_source_(std::move(incoming_stats_per_source)) {} + incoming_stats_per_source_(std::move(incoming_stats_per_source)), + overall_outgoing_stats_(GetOverallOutgoingStats()), + overall_incoming_stats_(GetOverallIncomingStats()) {} ~EmulatedNetworkStatsImpl() override = default; std::vector LocalAddresses() const override { @@ -166,59 +198,75 @@ class EmulatedNetworkStatsImpl final : public EmulatedNetworkStats { } int64_t PacketsSent() const override { - return GetOverallOutgoingStats()->PacketsSent(); + return overall_outgoing_stats_->PacketsSent(); } DataSize BytesSent() const override { - return GetOverallOutgoingStats()->BytesSent(); + return overall_outgoing_stats_->BytesSent(); + } + + const SamplesStatsCounter& SentPacketsSizeCounter() const override { + return overall_outgoing_stats_->SentPacketsSizeCounter(); + } + + const SamplesStatsCounter& SentPacketsQueueWaitTimeUs() const override { + return sent_packets_queue_wait_time_us_; } DataSize FirstSentPacketSize() const override { - return GetOverallOutgoingStats()->FirstSentPacketSize(); + return overall_outgoing_stats_->FirstSentPacketSize(); } Timestamp FirstPacketSentTime() const override { - return GetOverallOutgoingStats()->FirstPacketSentTime(); + return overall_outgoing_stats_->FirstPacketSentTime(); } Timestamp LastPacketSentTime() const override { - return GetOverallOutgoingStats()->LastPacketSentTime(); + return overall_outgoing_stats_->LastPacketSentTime(); } DataRate AverageSendRate() const override { - return GetOverallOutgoingStats()->AverageSendRate(); + return overall_outgoing_stats_->AverageSendRate(); } int64_t PacketsReceived() const override { - return GetOverallIncomingStats()->PacketsReceived(); + return overall_incoming_stats_->PacketsReceived(); } DataSize BytesReceived() const override { - return GetOverallIncomingStats()->BytesReceived(); + return overall_incoming_stats_->BytesReceived(); + } + + const SamplesStatsCounter& ReceivedPacketsSizeCounter() const override { + return overall_incoming_stats_->ReceivedPacketsSizeCounter(); } int64_t PacketsDropped() const override { - return GetOverallIncomingStats()->PacketsDropped(); + return overall_incoming_stats_->PacketsDropped(); } DataSize BytesDropped() const override { - return GetOverallIncomingStats()->BytesDropped(); + return overall_incoming_stats_->BytesDropped(); + } + + const SamplesStatsCounter& DroppedPacketsSizeCounter() const override { + return overall_incoming_stats_->DroppedPacketsSizeCounter(); } DataSize FirstReceivedPacketSize() const override { - return GetOverallIncomingStats()->FirstReceivedPacketSize(); + return overall_incoming_stats_->FirstReceivedPacketSize(); } Timestamp FirstPacketReceivedTime() const override { - return GetOverallIncomingStats()->FirstPacketReceivedTime(); + return overall_incoming_stats_->FirstPacketReceivedTime(); } Timestamp LastPacketReceivedTime() const override { - return GetOverallIncomingStats()->LastPacketReceivedTime(); + return overall_incoming_stats_->LastPacketReceivedTime(); } DataRate AverageReceiveRate() const override { - return GetOverallIncomingStats()->AverageReceiveRate(); + return overall_incoming_stats_->AverageReceiveRate(); } std::map> @@ -232,17 +280,22 @@ class EmulatedNetworkStatsImpl final : public EmulatedNetworkStats { std::unique_ptr GetOverallIncomingStats() const; const std::vector local_addresses_; + const SamplesStatsCounter sent_packets_queue_wait_time_us_; const std::map> outgoing_stats_per_destination_; const std::map> incoming_stats_per_source_; + const std::unique_ptr overall_outgoing_stats_; + const std::unique_ptr overall_incoming_stats_; }; class EmulatedNetworkOutgoingStatsBuilder { public: EmulatedNetworkOutgoingStatsBuilder(); - void OnPacketSent(Timestamp sent_time, DataSize packet_size); + void OnPacketSent(Timestamp sent_time, + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode); void AddOutgoingStats(const EmulatedNetworkOutgoingStats& stats); @@ -253,6 +306,8 @@ class EmulatedNetworkOutgoingStatsBuilder { int64_t packets_sent_ RTC_GUARDED_BY(sequence_checker_) = 0; DataSize bytes_sent_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero(); + SamplesStatsCounter sent_packets_size_counter_ + RTC_GUARDED_BY(sequence_checker_); DataSize first_sent_packet_size_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero(); Timestamp first_packet_sent_time_ RTC_GUARDED_BY(sequence_checker_) = @@ -265,9 +320,12 @@ class EmulatedNetworkIncomingStatsBuilder { public: EmulatedNetworkIncomingStatsBuilder(); - void OnPacketDropped(DataSize packet_size); + void OnPacketDropped(DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode); - void OnPacketReceived(Timestamp received_time, DataSize packet_size); + void OnPacketReceived(Timestamp received_time, + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode); // Adds stats collected from another endpoints to the builder. void AddIncomingStats(const EmulatedNetworkIncomingStats& stats); @@ -279,8 +337,12 @@ class EmulatedNetworkIncomingStatsBuilder { int64_t packets_received_ RTC_GUARDED_BY(sequence_checker_) = 0; DataSize bytes_received_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero(); + SamplesStatsCounter received_packets_size_counter_ + RTC_GUARDED_BY(sequence_checker_); int64_t packets_dropped_ RTC_GUARDED_BY(sequence_checker_) = 0; DataSize bytes_dropped_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero(); + SamplesStatsCounter dropped_packets_size_counter_ + RTC_GUARDED_BY(sequence_checker_); DataSize first_received_packet_size_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero(); Timestamp first_packet_received_time_ RTC_GUARDED_BY(sequence_checker_) = @@ -296,15 +358,20 @@ class EmulatedNetworkStatsBuilder { EmulatedNetworkStatsBuilder(); explicit EmulatedNetworkStatsBuilder(rtc::IPAddress local_ip); - void OnPacketSent(Timestamp sent_time, + void OnPacketSent(Timestamp queued_time, + Timestamp sent_time, rtc::IPAddress destination_ip, - DataSize packet_size); + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode); - void OnPacketDropped(rtc::IPAddress source_ip, DataSize packet_size); + void OnPacketDropped(rtc::IPAddress source_ip, + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode); void OnPacketReceived(Timestamp received_time, rtc::IPAddress source_ip, - DataSize packet_size); + DataSize packet_size, + EmulatedEndpointConfig::StatsGatheringMode mode); void AddEmulatedNetworkStats(const EmulatedNetworkStats& stats); @@ -315,6 +382,7 @@ class EmulatedNetworkStatsBuilder { std::vector local_addresses_ RTC_GUARDED_BY(sequence_checker_); + SamplesStatsCounter sent_packets_queue_wait_time_us_; std::map outgoing_stats_per_destination_ RTC_GUARDED_BY(sequence_checker_); std::map @@ -414,12 +482,14 @@ class EmulatedNetworkNode : public EmulatedNetworkReceiverInterface { // from other EmulatedNetworkNodes. class EmulatedEndpointImpl : public EmulatedEndpoint { public: - EmulatedEndpointImpl(uint64_t id, - const rtc::IPAddress& ip, - bool is_enabled, - rtc::AdapterType type, - rtc::TaskQueue* task_queue, - Clock* clock); + EmulatedEndpointImpl( + uint64_t id, + const rtc::IPAddress& ip, + EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode, + bool is_enabled, + rtc::AdapterType type, + rtc::TaskQueue* task_queue, + Clock* clock); ~EmulatedEndpointImpl() override; uint64_t GetId() const; @@ -456,9 +526,10 @@ class EmulatedEndpointImpl : public EmulatedEndpoint { rtc::RecursiveCriticalSection receiver_lock_; rtc::ThreadChecker enabled_state_checker_; - uint64_t id_; + const uint64_t id_; // Peer's local IP address for this endpoint network interface. const rtc::IPAddress peer_local_addr_; + const EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode_; bool is_enabled_ RTC_GUARDED_BY(enabled_state_checker_); const rtc::AdapterType type_; Clock* const clock_; diff --git a/test/network/network_emulation_manager.cc b/test/network/network_emulation_manager.cc index 1e8e5ef9ff..4a2e31e0f9 100644 --- a/test/network/network_emulation_manager.cc +++ b/test/network/network_emulation_manager.cc @@ -98,8 +98,8 @@ EmulatedEndpoint* NetworkEmulationManagerImpl::CreateEndpoint( bool res = used_ip_addresses_.insert(*ip).second; RTC_CHECK(res) << "IP=" << ip->ToString() << " already in use"; auto node = std::make_unique( - next_node_id_++, *ip, config.start_as_enabled, config.type, &task_queue_, - clock_); + next_node_id_++, *ip, config.stats_gathering_mode, + config.start_as_enabled, config.type, &task_queue_, clock_); EmulatedEndpoint* out = node.get(); endpoints_.push_back(std::move(node)); return out; diff --git a/test/network/network_emulation_unittest.cc b/test/network/network_emulation_unittest.cc index f9f088a693..c042f65864 100644 --- a/test/network/network_emulation_unittest.cc +++ b/test/network/network_emulation_unittest.cc @@ -273,6 +273,16 @@ TEST(NetworkEmulationManagerTest, Run) { EXPECT_EQ(dest_st.at(bob_ip)->PacketsSent(), 2000l); EXPECT_EQ(dest_st.at(bob_ip)->BytesSent().bytes(), single_packet_size * 2000l); + + // No debug stats are collected by default. + EXPECT_TRUE(st->SentPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(st->SentPacketsQueueWaitTimeUs().IsEmpty()); + EXPECT_TRUE(st->ReceivedPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(dest_st.at(bob_ip)->SentPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(source_st.at(bob_ip)->DroppedPacketsSizeCounter().IsEmpty()); + received_stats_count++; }); nt2->GetStats([&](std::unique_ptr st) { @@ -304,6 +314,16 @@ TEST(NetworkEmulationManagerTest, Run) { EXPECT_EQ(dest_st.at(alice_ip)->PacketsSent(), 2000l); EXPECT_EQ(dest_st.at(alice_ip)->BytesSent().bytes(), single_packet_size * 2000l); + + // No debug stats are collected by default. + EXPECT_TRUE(st->SentPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(st->SentPacketsQueueWaitTimeUs().IsEmpty()); + EXPECT_TRUE(st->ReceivedPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(dest_st.at(alice_ip)->SentPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(source_st.at(alice_ip)->ReceivedPacketsSizeCounter().IsEmpty()); + EXPECT_TRUE(source_st.at(alice_ip)->DroppedPacketsSizeCounter().IsEmpty()); + received_stats_count++; }); ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 2, @@ -311,6 +331,105 @@ TEST(NetworkEmulationManagerTest, Run) { *network_manager.time_controller()); } +TEST(NetworkEmulationManagerTest, DebugStatsCollectedInDebugMode) { + NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated); + + EmulatedNetworkNode* alice_node = network_manager.CreateEmulatedNode( + std::make_unique(BuiltInNetworkBehaviorConfig())); + EmulatedNetworkNode* bob_node = network_manager.CreateEmulatedNode( + std::make_unique(BuiltInNetworkBehaviorConfig())); + EmulatedEndpointConfig debug_config; + debug_config.stats_gathering_mode = + EmulatedEndpointConfig::StatsGatheringMode::kDebug; + EmulatedEndpoint* alice_endpoint = + network_manager.CreateEndpoint(debug_config); + EmulatedEndpoint* bob_endpoint = + network_manager.CreateEndpoint(EmulatedEndpointConfig()); + network_manager.CreateRoute(alice_endpoint, {alice_node}, bob_endpoint); + network_manager.CreateRoute(bob_endpoint, {bob_node}, alice_endpoint); + + EmulatedNetworkManagerInterface* nt1 = + network_manager.CreateEmulatedNetworkManagerInterface({alice_endpoint}); + EmulatedNetworkManagerInterface* nt2 = + network_manager.CreateEmulatedNetworkManagerInterface({bob_endpoint}); + + rtc::Thread* t1 = nt1->network_thread(); + rtc::Thread* t2 = nt2->network_thread(); + + rtc::CopyOnWriteBuffer data("Hello"); + for (uint64_t j = 0; j < 2; j++) { + auto* s1 = t1->socketserver()->CreateAsyncSocket(AF_INET, SOCK_DGRAM); + auto* s2 = t2->socketserver()->CreateAsyncSocket(AF_INET, SOCK_DGRAM); + + SocketReader r1(s1, t1); + SocketReader r2(s2, t2); + + rtc::SocketAddress a1(alice_endpoint->GetPeerLocalAddress(), 0); + rtc::SocketAddress a2(bob_endpoint->GetPeerLocalAddress(), 0); + + t1->Invoke(RTC_FROM_HERE, [&] { + s1->Bind(a1); + a1 = s1->GetLocalAddress(); + }); + t2->Invoke(RTC_FROM_HERE, [&] { + s2->Bind(a2); + a2 = s2->GetLocalAddress(); + }); + + t1->Invoke(RTC_FROM_HERE, [&] { s1->Connect(a2); }); + t2->Invoke(RTC_FROM_HERE, [&] { s2->Connect(a1); }); + + for (uint64_t i = 0; i < 1000; i++) { + t1->PostTask(RTC_FROM_HERE, + [&]() { s1->Send(data.data(), data.size()); }); + t2->PostTask(RTC_FROM_HERE, + [&]() { s2->Send(data.data(), data.size()); }); + } + + network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1)); + + EXPECT_EQ(r1.ReceivedCount(), 1000); + EXPECT_EQ(r2.ReceivedCount(), 1000); + + t1->Invoke(RTC_FROM_HERE, [&] { delete s1; }); + t2->Invoke(RTC_FROM_HERE, [&] { delete s2; }); + } + + const int64_t single_packet_size = data.size() + kOverheadIpv4Udp; + std::atomic received_stats_count{0}; + nt1->GetStats([&](std::unique_ptr st) { + rtc::IPAddress bob_ip = bob_endpoint->GetPeerLocalAddress(); + std::map> + source_st = st->IncomingStatsPerSource(); + ASSERT_EQ(source_st.size(), 1lu); + + std::map> + dest_st = st->OutgoingStatsPerDestination(); + ASSERT_EQ(dest_st.size(), 1lu); + + // No debug stats are collected by default. + EXPECT_EQ(st->SentPacketsSizeCounter().NumSamples(), 2000l); + EXPECT_EQ(st->ReceivedPacketsSizeCounter().GetAverage(), + single_packet_size); + EXPECT_EQ(st->SentPacketsQueueWaitTimeUs().NumSamples(), 2000l); + EXPECT_LT(st->SentPacketsQueueWaitTimeUs().GetMax(), 1); + EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty()); + EXPECT_EQ(dest_st.at(bob_ip)->SentPacketsSizeCounter().NumSamples(), 2000l); + EXPECT_EQ(dest_st.at(bob_ip)->SentPacketsSizeCounter().GetAverage(), + single_packet_size); + EXPECT_EQ(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().NumSamples(), + 2000l); + EXPECT_EQ(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().GetAverage(), + single_packet_size); + EXPECT_TRUE(source_st.at(bob_ip)->DroppedPacketsSizeCounter().IsEmpty()); + + received_stats_count++; + }); + ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 1, + kStatsWaitTimeout.ms(), + *network_manager.time_controller()); +} + TEST(NetworkEmulationManagerTest, ThroughputStats) { NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime); diff --git a/test/pc/e2e/BUILD.gn b/test/pc/e2e/BUILD.gn index e00ab36796..f9c0c9b4da 100644 --- a/test/pc/e2e/BUILD.gn +++ b/test/pc/e2e/BUILD.gn @@ -696,6 +696,7 @@ if (!build_with_chromium) { "../../../api:peer_connection_quality_test_fixture_api", "../../../api:rtc_stats_api", "../../../api:scoped_refptr", + "../../../api/numerics", "../../../api/test/network_emulation", "../../../api/units:data_rate", "../../../api/units:data_size", diff --git a/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc b/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc index 9f4283dd0a..eea83970fc 100644 --- a/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc +++ b/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc @@ -250,6 +250,16 @@ void StatsBasedNetworkQualityMetricsReporter::ReportResult( /*important=*/false); } +void StatsBasedNetworkQualityMetricsReporter::ReportResult( + const std::string& metric_name, + const std::string& network_label, + const SamplesStatsCounter& value, + const std::string& unit) const { + test::PrintResult(metric_name, /*modifier=*/"", + GetTestCaseName(network_label), value, unit, + /*important=*/false); +} + std::string StatsBasedNetworkQualityMetricsReporter::GetTestCaseName( absl::string_view network_label) const { rtc::StringBuilder builder; @@ -258,7 +268,7 @@ std::string StatsBasedNetworkQualityMetricsReporter::GetTestCaseName( } void StatsBasedNetworkQualityMetricsReporter::LogNetworkLayerStats( - absl::string_view peer_name, + const std::string& peer_name, const NetworkLayerStats& stats) const { DataRate average_send_rate = stats.stats->PacketsSent() >= 2 ? stats.stats->AverageSendRate() @@ -273,6 +283,19 @@ void StatsBasedNetworkQualityMetricsReporter::LogNetworkLayerStats( for (size_t i = 0; i < local_ips.size(); ++i) { log << " " << local_ips[i].ToString() << "\n"; } + if (!stats.stats->SentPacketsSizeCounter().IsEmpty()) { + ReportResult("sent_packets_size", peer_name, + stats.stats->SentPacketsSizeCounter(), "sizeInBytes"); + } + if (!stats.stats->ReceivedPacketsSizeCounter().IsEmpty()) { + ReportResult("received_packets_size", peer_name, + stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes"); + } + if (!stats.stats->DroppedPacketsSizeCounter().IsEmpty()) { + ReportResult("dropped_packets_size", peer_name, + stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes"); + } + log << "Send statistic:\n" << " packets: " << stats.stats->PacketsSent() << " bytes: " << stats.stats->BytesSent().bytes() @@ -289,6 +312,11 @@ void StatsBasedNetworkQualityMetricsReporter::LogNetworkLayerStats( << " bytes: " << entry.second->BytesSent().bytes() << " avg_rate (bytes/sec): " << source_average_send_rate.bytes_per_sec() << " avg_rate (bps): " << source_average_send_rate.bps() << "\n"; + if (!entry.second->SentPacketsSizeCounter().IsEmpty()) { + ReportResult("sent_packets_size", + peer_name + "/" + entry.first.ToString(), + stats.stats->SentPacketsSizeCounter(), "sizeInBytes"); + } } log << "Receive statistic:\n" @@ -309,6 +337,16 @@ void StatsBasedNetworkQualityMetricsReporter::LogNetworkLayerStats( << " avg_rate (bytes/sec): " << source_average_receive_rate.bytes_per_sec() << " avg_rate (bps): " << source_average_receive_rate.bps() << "\n"; + if (!entry.second->ReceivedPacketsSizeCounter().IsEmpty()) { + ReportResult("received_packets_size", + peer_name + "/" + entry.first.ToString(), + stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes"); + } + if (!entry.second->DroppedPacketsSizeCounter().IsEmpty()) { + ReportResult("dropped_packets_size", + peer_name + "/" + entry.first.ToString(), + stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes"); + } } RTC_LOG(INFO) << log.str(); diff --git a/test/pc/e2e/stats_based_network_quality_metrics_reporter.h b/test/pc/e2e/stats_based_network_quality_metrics_reporter.h index f01a49d6da..d14bb43e1b 100644 --- a/test/pc/e2e/stats_based_network_quality_metrics_reporter.h +++ b/test/pc/e2e/stats_based_network_quality_metrics_reporter.h @@ -20,6 +20,7 @@ #include #include "absl/strings/string_view.h" +#include "api/numerics/samples_stats_counter.h" #include "api/test/network_emulation/network_emulation_interfaces.h" #include "api/test/network_emulation_manager.h" #include "api/test/peerconnection_quality_test_fixture.h" @@ -95,8 +96,12 @@ class StatsBasedNetworkQualityMetricsReporter const std::string& network_label, const double value, const std::string& unit) const; + void ReportResult(const std::string& metric_name, + const std::string& network_label, + const SamplesStatsCounter& value, + const std::string& unit) const; std::string GetTestCaseName(absl::string_view network_label) const; - void LogNetworkLayerStats(absl::string_view peer_name, + void LogNetworkLayerStats(const std::string& peer_name, const NetworkLayerStats& stats) const; NetworkLayerStatsCollector collector_;