Add counter of ECN marking to EmulatedNetwork stats

Bug: webrtc:42225697
Change-Id: I99c68afafe20fcdbc785d489a8b484cec3b3987d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/368941
Reviewed-by: Artem Titov <titovartem@webrtc.org>
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43455}
This commit is contained in:
Per Kjellander 2024-11-25 19:57:13 +00:00 committed by WebRTC LUCI CQ
parent ff88950833
commit 0a69daf38b
7 changed files with 149 additions and 41 deletions

View File

@ -38,6 +38,8 @@ rtc_library("network_emulation") {
sources = [ sources = [
"cross_traffic.h", "cross_traffic.h",
"ecn_marking_counter.cc",
"ecn_marking_counter.h",
"network_emulation_interfaces.cc", "network_emulation_interfaces.cc",
"network_emulation_interfaces.h", "network_emulation_interfaces.h",
] ]

View File

@ -0,0 +1,40 @@
/*
* 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/test/network_emulation/ecn_marking_counter.h"
namespace webrtc {
void EcnMarkingCounter::Add(EcnMarking ecn) {
switch (ecn) {
case EcnMarking::kNotEct:
++not_ect_;
break;
case EcnMarking::kEct0:
++ect_0_;
break;
case EcnMarking::kEct1:
++ect_1_;
break;
case EcnMarking::kCe:
++ce_;
break;
}
}
EcnMarkingCounter& EcnMarkingCounter::operator+=(
const EcnMarkingCounter& counter) {
not_ect_ += counter.not_ect();
ect_0_ += counter.ect_0();
ect_1_ += counter.ect_1();
ce_ += counter.ce();
return *this;
}
} // namespace webrtc

View File

@ -0,0 +1,41 @@
/*
* 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_TEST_NETWORK_EMULATION_ECN_MARKING_COUNTER_H_
#define API_TEST_NETWORK_EMULATION_ECN_MARKING_COUNTER_H_
#include "api/transport/ecn_marking.h"
namespace webrtc {
// Counts Explicit Congestion Notifaction marks in IP packets.
// https://www.rfc-editor.org/rfc/rfc9331.html
class EcnMarkingCounter {
public:
// Number of packets without ECT explicitly set sent through the network.
int not_ect() const { return not_ect_; }
// Number of packets with ECT(1) sent through the network.
int ect_0() const { return ect_0_; }
// Number of packets with ECT(1) sent through the network.
int ect_1() const { return ect_1_; }
// Number of packets the network has marked as CE (congestion experienced).
int ce() const { return ce_; }
void Add(EcnMarking ecn);
EcnMarkingCounter& operator+=(const EcnMarkingCounter& counter);
private:
int not_ect_ = 0;
int ect_0_ = 0; // Not used by WebRTC or L4S.
int ect_1_ = 0;
int ce_ = 0;
};
} // namespace webrtc
#endif // API_TEST_NETWORK_EMULATION_ECN_MARKING_COUNTER_H_

View File

@ -18,6 +18,7 @@
#include <vector> #include <vector>
#include "api/numerics/samples_stats_counter.h" #include "api/numerics/samples_stats_counter.h"
#include "api/test/network_emulation/ecn_marking_counter.h"
#include "api/transport/ecn_marking.h" #include "api/transport/ecn_marking.h"
#include "api/units/data_rate.h" #include "api/units/data_rate.h"
#include "api/units/data_size.h" #include "api/units/data_size.h"
@ -83,6 +84,8 @@ struct EmulatedNetworkOutgoingStats {
// Time of the last packet sent or infinite value if no packets were sent. // Time of the last packet sent or infinite value if no packets were sent.
Timestamp last_packet_sent_time = Timestamp::MinusInfinity(); Timestamp last_packet_sent_time = Timestamp::MinusInfinity();
EcnMarkingCounter ecn_count;
// Returns average send rate. Requires that at least 2 packets were sent. // Returns average send rate. Requires that at least 2 packets were sent.
DataRate AverageSendRate() const; DataRate AverageSendRate() const;
}; };
@ -118,6 +121,8 @@ struct EmulatedNetworkIncomingStats {
// received. // received.
Timestamp last_packet_received_time = Timestamp::MinusInfinity(); Timestamp last_packet_received_time = Timestamp::MinusInfinity();
EcnMarkingCounter ecn_count;
DataRate AverageReceiveRate() const; DataRate AverageReceiveRate() const;
}; };

View File

@ -31,6 +31,7 @@
#include "api/transport/ecn_marking.h" #include "api/transport/ecn_marking.h"
#include "api/units/data_size.h" #include "api/units/data_size.h"
#include "api/units/time_delta.h" #include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "rtc_base/logging.h" #include "rtc_base/logging.h"
namespace webrtc { namespace webrtc {
@ -77,19 +78,21 @@ EmulatedNetworkOutgoingStatsBuilder::EmulatedNetworkOutgoingStatsBuilder(
sequence_checker_.Detach(); sequence_checker_.Detach();
} }
void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(Timestamp sent_time, void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(
DataSize packet_size) { Timestamp sent_time,
const EmulatedIpPacket& packet) {
RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_DCHECK_RUN_ON(&sequence_checker_);
RTC_CHECK_GE(packet_size, DataSize::Zero()); RTC_CHECK_GE(packet.size(), 0);
if (stats_.first_packet_sent_time.IsInfinite()) { if (stats_.first_packet_sent_time.IsInfinite()) {
stats_.first_packet_sent_time = sent_time; stats_.first_packet_sent_time = sent_time;
stats_.first_sent_packet_size = packet_size; stats_.first_sent_packet_size = DataSize::Bytes(packet.ip_packet_size());
} }
stats_.last_packet_sent_time = sent_time; stats_.last_packet_sent_time = sent_time;
stats_.packets_sent++; stats_.packets_sent++;
stats_.bytes_sent += packet_size; stats_.bytes_sent += DataSize::Bytes(packet.ip_packet_size());
stats_.ecn_count.Add(packet.ecn);
if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) { if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
stats_.sent_packets_size.AddSample(packet_size.bytes()); stats_.sent_packets_size.AddSample(packet.ip_packet_size());
} }
} }
@ -106,6 +109,7 @@ void EmulatedNetworkOutgoingStatsBuilder::AddOutgoingStats(
if (stats_.last_packet_sent_time < stats.last_packet_sent_time) { if (stats_.last_packet_sent_time < stats.last_packet_sent_time) {
stats_.last_packet_sent_time = stats.last_packet_sent_time; stats_.last_packet_sent_time = stats.last_packet_sent_time;
} }
stats_.ecn_count += stats.ecn_count;
} }
EmulatedNetworkOutgoingStats EmulatedNetworkOutgoingStatsBuilder::Build() EmulatedNetworkOutgoingStats EmulatedNetworkOutgoingStatsBuilder::Build()
@ -132,18 +136,20 @@ void EmulatedNetworkIncomingStatsBuilder::OnPacketDropped(
void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived( void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived(
Timestamp received_time, Timestamp received_time,
DataSize packet_size) { const EmulatedIpPacket& packet) {
RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_DCHECK_RUN_ON(&sequence_checker_);
RTC_CHECK_GE(packet_size, DataSize::Zero()); RTC_CHECK_GE(packet.size(), 0);
if (stats_.first_packet_received_time.IsInfinite()) { if (stats_.first_packet_received_time.IsInfinite()) {
stats_.first_packet_received_time = received_time; stats_.first_packet_received_time = received_time;
stats_.first_received_packet_size = packet_size; stats_.first_received_packet_size =
DataSize::Bytes(packet.ip_packet_size());
} }
stats_.last_packet_received_time = received_time; stats_.last_packet_received_time = received_time;
stats_.packets_received++; stats_.packets_received++;
stats_.bytes_received += packet_size; stats_.ecn_count.Add(packet.ecn);
stats_.bytes_received += DataSize::Bytes(packet.ip_packet_size());
if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) { if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
stats_.received_packets_size.AddSample(packet_size.bytes()); stats_.received_packets_size.AddSample(packet.ip_packet_size());
} }
} }
@ -164,6 +170,7 @@ void EmulatedNetworkIncomingStatsBuilder::AddIncomingStats(
if (stats_.last_packet_received_time < stats.last_packet_received_time) { if (stats_.last_packet_received_time < stats.last_packet_received_time) {
stats_.last_packet_received_time = stats.last_packet_received_time; stats_.last_packet_received_time = stats.last_packet_received_time;
} }
stats_.ecn_count += stats.ecn_count;
} }
EmulatedNetworkIncomingStats EmulatedNetworkIncomingStatsBuilder::Build() EmulatedNetworkIncomingStats EmulatedNetworkIncomingStatsBuilder::Build()
@ -186,23 +193,22 @@ EmulatedNetworkStatsBuilder::EmulatedNetworkStatsBuilder(
sequence_checker_.Detach(); sequence_checker_.Detach();
} }
void EmulatedNetworkStatsBuilder::OnPacketSent(Timestamp queued_time, void EmulatedNetworkStatsBuilder::OnPacketSent(Timestamp sent_time,
Timestamp sent_time, const EmulatedIpPacket& packet) {
rtc::IPAddress destination_ip,
DataSize packet_size) {
RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_DCHECK_RUN_ON(&sequence_checker_);
if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) { if (stats_gathering_mode_ == EmulatedNetworkStatsGatheringMode::kDebug) {
sent_packets_queue_wait_time_us_.AddSample((sent_time - queued_time).us()); sent_packets_queue_wait_time_us_.AddSample(
(sent_time - packet.arrival_time).us());
} }
auto it = outgoing_stats_per_destination_.find(destination_ip); auto it = outgoing_stats_per_destination_.find(packet.to.ipaddr());
if (it == outgoing_stats_per_destination_.end()) { if (it == outgoing_stats_per_destination_.end()) {
outgoing_stats_per_destination_ outgoing_stats_per_destination_
.emplace(destination_ip, .emplace(packet.to.ipaddr(),
std::make_unique<EmulatedNetworkOutgoingStatsBuilder>( std::make_unique<EmulatedNetworkOutgoingStatsBuilder>(
stats_gathering_mode_)) stats_gathering_mode_))
.first->second->OnPacketSent(sent_time, packet_size); .first->second->OnPacketSent(sent_time, packet);
} else { } else {
it->second->OnPacketSent(sent_time, packet_size); it->second->OnPacketSent(sent_time, packet);
} }
} }
@ -221,19 +227,19 @@ void EmulatedNetworkStatsBuilder::OnPacketDropped(rtc::IPAddress source_ip,
} }
} }
void EmulatedNetworkStatsBuilder::OnPacketReceived(Timestamp received_time, void EmulatedNetworkStatsBuilder::OnPacketReceived(
rtc::IPAddress source_ip, Timestamp received_time,
DataSize packet_size) { const EmulatedIpPacket& packet) {
RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_DCHECK_RUN_ON(&sequence_checker_);
auto it = incoming_stats_per_source_.find(source_ip); auto it = incoming_stats_per_source_.find(packet.from.ipaddr());
if (it == incoming_stats_per_source_.end()) { if (it == incoming_stats_per_source_.end()) {
incoming_stats_per_source_ incoming_stats_per_source_
.emplace(source_ip, .emplace(packet.from.ipaddr(),
std::make_unique<EmulatedNetworkIncomingStatsBuilder>( std::make_unique<EmulatedNetworkIncomingStatsBuilder>(
stats_gathering_mode_)) stats_gathering_mode_))
.first->second->OnPacketReceived(received_time, packet_size); .first->second->OnPacketReceived(received_time, packet);
} else { } else {
it->second->OnPacketReceived(received_time, packet_size); it->second->OnPacketReceived(received_time, packet);
} }
} }
@ -627,10 +633,7 @@ void EmulatedEndpointImpl::SendPacket(const rtc::SocketAddress& from,
clock_->CurrentTime(), application_overhead, ecn); clock_->CurrentTime(), application_overhead, ecn);
task_queue_->PostTask([this, packet = std::move(packet)]() mutable { task_queue_->PostTask([this, packet = std::move(packet)]() mutable {
RTC_DCHECK_RUN_ON(task_queue_); RTC_DCHECK_RUN_ON(task_queue_);
stats_builder_.OnPacketSent(packet.arrival_time, clock_->CurrentTime(), stats_builder_.OnPacketSent(clock_->CurrentTime(), packet);
packet.to.ipaddr(),
DataSize::Bytes(packet.ip_packet_size()));
if (packet.to.ipaddr() == options_.ip) { if (packet.to.ipaddr() == options_.ip) {
OnPacketReceived(std::move(packet)); OnPacketReceived(std::move(packet));
} else { } else {
@ -734,8 +737,7 @@ void EmulatedEndpointImpl::OnPacketReceived(EmulatedIpPacket packet) {
<< "; Receiver options_.ip=" << options_.ip.ToString(); << "; Receiver options_.ip=" << options_.ip.ToString();
} }
MutexLock lock(&receiver_lock_); MutexLock lock(&receiver_lock_);
stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet.from.ipaddr(), stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet);
DataSize::Bytes(packet.ip_packet_size()));
auto it = port_to_receiver_.find(packet.to.port()); auto it = port_to_receiver_.find(packet.to.port());
if (it == port_to_receiver_.end()) { if (it == port_to_receiver_.end()) {
if (default_receiver_.has_value()) { if (default_receiver_.has_value()) {

View File

@ -52,7 +52,7 @@ class EmulatedNetworkOutgoingStatsBuilder {
explicit EmulatedNetworkOutgoingStatsBuilder( explicit EmulatedNetworkOutgoingStatsBuilder(
EmulatedNetworkStatsGatheringMode stats_gathering_mode); EmulatedNetworkStatsGatheringMode stats_gathering_mode);
void OnPacketSent(Timestamp sent_time, DataSize packet_size); void OnPacketSent(Timestamp sent_time, const EmulatedIpPacket& packet);
void AddOutgoingStats(const EmulatedNetworkOutgoingStats& stats); void AddOutgoingStats(const EmulatedNetworkOutgoingStats& stats);
@ -74,7 +74,8 @@ class EmulatedNetworkIncomingStatsBuilder {
void OnPacketDropped(DataSize packet_size); void OnPacketDropped(DataSize packet_size);
void OnPacketReceived(Timestamp received_time, DataSize packet_size); void OnPacketReceived(Timestamp received_time,
const EmulatedIpPacket& packet);
// Adds stats collected from another endpoints to the builder. // Adds stats collected from another endpoints to the builder.
void AddIncomingStats(const EmulatedNetworkIncomingStats& stats); void AddIncomingStats(const EmulatedNetworkIncomingStats& stats);
@ -98,16 +99,12 @@ class EmulatedNetworkStatsBuilder {
rtc::IPAddress local_ip, rtc::IPAddress local_ip,
EmulatedNetworkStatsGatheringMode stats_gathering_mode); EmulatedNetworkStatsGatheringMode stats_gathering_mode);
void OnPacketSent(Timestamp queued_time, void OnPacketSent(Timestamp send_time, const EmulatedIpPacket& packet);
Timestamp sent_time,
rtc::IPAddress destination_ip,
DataSize packet_size);
void OnPacketDropped(rtc::IPAddress source_ip, DataSize packet_size); void OnPacketDropped(rtc::IPAddress source_ip, DataSize packet_size);
void OnPacketReceived(Timestamp received_time, void OnPacketReceived(Timestamp received_time,
rtc::IPAddress source_ip, const EmulatedIpPacket& packet);
DataSize packet_size);
void AddEmulatedNetworkStats(const EmulatedNetworkStats& stats); void AddEmulatedNetworkStats(const EmulatedNetworkStats& stats);

View File

@ -423,6 +423,27 @@ TEST(NetworkEmulationManagerTest, EcnMarkingIsPropagated) {
EXPECT_EQ(r2.ReceivedCount(), 1); EXPECT_EQ(r2.ReceivedCount(), 1);
EXPECT_EQ(r2.LastEcnMarking(), webrtc::EcnMarking::kEct1); EXPECT_EQ(r2.LastEcnMarking(), webrtc::EcnMarking::kEct1);
std::atomic<int> received_stats_count{0};
nt1->GetStats([&](EmulatedNetworkStats st) {
EXPECT_EQ(st.overall_incoming_stats.packets_received, 0);
EXPECT_EQ(st.overall_outgoing_stats.packets_sent, 1);
EXPECT_EQ(st.overall_outgoing_stats.ecn_count.ect_1(), 1);
EXPECT_EQ(st.overall_outgoing_stats.ecn_count.ce(), 0);
EXPECT_EQ(st.overall_outgoing_stats.ecn_count.not_ect(), 0);
++received_stats_count;
});
nt2->GetStats([&](EmulatedNetworkStats st) {
EXPECT_EQ(st.overall_incoming_stats.packets_received, 1);
EXPECT_EQ(st.overall_outgoing_stats.packets_sent, 0);
EXPECT_EQ(st.overall_incoming_stats.ecn_count.ect_1(), 1);
EXPECT_EQ(st.overall_incoming_stats.ecn_count.ce(), 0);
EXPECT_EQ(st.overall_incoming_stats.ecn_count.not_ect(), 0);
++received_stats_count;
});
ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 2,
kStatsWaitTimeout.ms(),
*network_manager.time_controller());
SendTask(t1, [&] { delete s1; }); SendTask(t1, [&] { delete s1; });
SendTask(t2, [&] { delete s2; }); SendTask(t2, [&] { delete s2; });
} }