dcsctp: Add retransmission counters to metrics
Bug: webrtc:15458 Change-Id: Ib90cb0b9a94e1f358685ed319538654b0c8ed5c4 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/318581 Reviewed-by: Florent Castelli <orphis@webrtc.org> Commit-Queue: Victor Boivie <boivie@webrtc.org> Cr-Commit-Position: refs/heads/main@{#40683}
This commit is contained in:
parent
905197174f
commit
a7c6de9068
@ -211,6 +211,16 @@ struct Metrics {
|
||||
// Number of messages requested to be sent.
|
||||
size_t tx_messages_count = 0;
|
||||
|
||||
// Number of packets retransmitted. Since SCTP packets can contain both
|
||||
// retransmitted DATA chunks and DATA chunks that are transmitted for the
|
||||
// first time, this represents an upper bound as it's incremented every time a
|
||||
// packet contains a retransmitted DATA chunk.
|
||||
size_t rtx_packets_count = 0;
|
||||
|
||||
// Total number of bytes retransmitted. This includes the payload and
|
||||
// DATA/I-DATA headers, but not SCTP packet headers.
|
||||
uint64_t rtx_bytes_count = 0;
|
||||
|
||||
// The current congestion window (cwnd) in bytes, corresponding to spinfo_cwnd
|
||||
// defined in RFC6458.
|
||||
size_t cwnd_bytes = 0;
|
||||
@ -583,7 +593,9 @@ class DcSctpSocketInterface {
|
||||
size_t bytes) = 0;
|
||||
|
||||
// Retrieves the latest metrics. If the socket is not fully connected,
|
||||
// `absl::nullopt` will be returned.
|
||||
// `absl::nullopt` will be returned. Note that metrics are not guaranteed to
|
||||
// be carried over if this socket is handed over by calling
|
||||
// `GetHandoverStateAndClose`.
|
||||
virtual absl::optional<Metrics> GetMetrics() const = 0;
|
||||
|
||||
// Returns empty bitmask if the socket is in the state in which a snapshot of
|
||||
|
||||
@ -244,6 +244,7 @@ if (rtc_include_tests) {
|
||||
"../../../test:test_support",
|
||||
"../common:handover_testing",
|
||||
"../common:internal_types",
|
||||
"../common:math",
|
||||
"../packet:chunk",
|
||||
"../packet:error_cause",
|
||||
"../packet:parameter",
|
||||
|
||||
@ -598,6 +598,8 @@ absl::optional<Metrics> DcSctpSocket::GetMetrics() const {
|
||||
tcb_->capabilities().negotiated_maximum_incoming_streams;
|
||||
metrics.negotiated_maximum_incoming_streams =
|
||||
tcb_->capabilities().negotiated_maximum_incoming_streams;
|
||||
metrics.rtx_packets_count = tcb_->retransmission_queue().rtx_packets_count();
|
||||
metrics.rtx_bytes_count = tcb_->retransmission_queue().rtx_bytes_count();
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "net/dcsctp/common/handover_testing.h"
|
||||
#include "net/dcsctp/common/math.h"
|
||||
#include "net/dcsctp/packet/chunk/chunk.h"
|
||||
#include "net/dcsctp/packet/chunk/cookie_echo_chunk.h"
|
||||
#include "net/dcsctp/packet/chunk/data_chunk.h"
|
||||
@ -2032,6 +2033,44 @@ TEST(DcSctpSocketTest, RxAndTxPacketMetricsIncrease) {
|
||||
EXPECT_EQ(a.socket.GetMetrics()->peer_rwnd_bytes, initial_a_rwnd);
|
||||
}
|
||||
|
||||
TEST(DcSctpSocketTest, RetransmissionMetricsAreSetForFastRetransmit) {
|
||||
SocketUnderTest a("A");
|
||||
SocketUnderTest z("Z");
|
||||
ConnectSockets(a, z);
|
||||
|
||||
// Enough to trigger fast retransmit of the missing second packet.
|
||||
std::vector<uint8_t> payload(DcSctpOptions::kMaxSafeMTUSize * 5);
|
||||
a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions);
|
||||
|
||||
// Receive first packet, drop second, receive and retransmit the remaining.
|
||||
z.socket.ReceivePacket(a.cb.ConsumeSentPacket());
|
||||
a.cb.ConsumeSentPacket();
|
||||
ExchangeMessages(a, z);
|
||||
|
||||
EXPECT_EQ(a.socket.GetMetrics()->rtx_packets_count, 1u);
|
||||
size_t expected_data_size =
|
||||
RoundDownTo4(DcSctpOptions::kMaxSafeMTUSize - SctpPacket::kHeaderSize);
|
||||
EXPECT_EQ(a.socket.GetMetrics()->rtx_bytes_count, expected_data_size);
|
||||
}
|
||||
|
||||
TEST(DcSctpSocketTest, RetransmissionMetricsAreSetForNormalRetransmit) {
|
||||
SocketUnderTest a("A");
|
||||
SocketUnderTest z("Z");
|
||||
ConnectSockets(a, z);
|
||||
|
||||
std::vector<uint8_t> payload(kSmallMessageSize);
|
||||
a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions);
|
||||
|
||||
a.cb.ConsumeSentPacket();
|
||||
AdvanceTime(a, z, a.options.rto_initial);
|
||||
ExchangeMessages(a, z);
|
||||
|
||||
EXPECT_EQ(a.socket.GetMetrics()->rtx_packets_count, 1u);
|
||||
size_t expected_data_size =
|
||||
RoundUpTo4(kSmallMessageSize + DataChunk::kHeaderSize);
|
||||
EXPECT_EQ(a.socket.GetMetrics()->rtx_bytes_count, expected_data_size);
|
||||
}
|
||||
|
||||
TEST_P(DcSctpSocketParametrizedTest, UnackDataAlsoIncludesSendQueue) {
|
||||
SocketUnderTest a("A");
|
||||
auto z = std::make_unique<SocketUnderTest>("Z");
|
||||
|
||||
@ -426,18 +426,21 @@ RetransmissionQueue::GetChunksForFastRetransmit(size_t bytes_in_packet) {
|
||||
if (!t3_rtx_.is_running()) {
|
||||
t3_rtx_.Start();
|
||||
}
|
||||
|
||||
size_t bytes_retransmitted = absl::c_accumulate(
|
||||
to_be_sent, 0, [&](size_t r, const std::pair<TSN, Data>& d) {
|
||||
return r + GetSerializedChunkSize(d.second);
|
||||
});
|
||||
++rtx_packets_count_;
|
||||
rtx_bytes_count_ += bytes_retransmitted;
|
||||
|
||||
RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Fast-retransmitting TSN "
|
||||
<< StrJoin(to_be_sent, ",",
|
||||
[&](rtc::StringBuilder& sb,
|
||||
const std::pair<TSN, Data>& c) {
|
||||
sb << *c.first;
|
||||
})
|
||||
<< " - "
|
||||
<< absl::c_accumulate(
|
||||
to_be_sent, 0,
|
||||
[&](size_t r, const std::pair<TSN, Data>& d) {
|
||||
return r + GetSerializedChunkSize(d.second);
|
||||
})
|
||||
<< " - " << bytes_retransmitted
|
||||
<< " bytes. outstanding_bytes=" << outstanding_bytes()
|
||||
<< " (" << old_outstanding_bytes << ")";
|
||||
|
||||
@ -463,10 +466,17 @@ std::vector<std::pair<TSN, Data>> RetransmissionQueue::GetChunksToSend(
|
||||
RoundDownTo4(std::min(max_bytes_to_send(), bytes_remaining_in_packet));
|
||||
|
||||
to_be_sent = outstanding_data_.GetChunksToBeRetransmitted(max_bytes);
|
||||
max_bytes -= absl::c_accumulate(to_be_sent, 0,
|
||||
[&](size_t r, const std::pair<TSN, Data>& d) {
|
||||
|
||||
size_t bytes_retransmitted = absl::c_accumulate(
|
||||
to_be_sent, 0, [&](size_t r, const std::pair<TSN, Data>& d) {
|
||||
return r + GetSerializedChunkSize(d.second);
|
||||
});
|
||||
max_bytes -= bytes_retransmitted;
|
||||
|
||||
if (!to_be_sent.empty()) {
|
||||
++rtx_packets_count_;
|
||||
rtx_bytes_count_ += bytes_retransmitted;
|
||||
}
|
||||
|
||||
while (max_bytes > data_chunk_header_size_) {
|
||||
RTC_DCHECK(IsDivisibleBy4(max_bytes));
|
||||
|
||||
@ -113,6 +113,9 @@ class RetransmissionQueue {
|
||||
// Returns the current receiver window size.
|
||||
size_t rwnd() const { return rwnd_; }
|
||||
|
||||
size_t rtx_packets_count() const { return rtx_packets_count_; }
|
||||
uint64_t rtx_bytes_count() const { return rtx_bytes_count_; }
|
||||
|
||||
// Returns the number of bytes of packets that are in-flight.
|
||||
size_t outstanding_bytes() const {
|
||||
return outstanding_data_.outstanding_bytes();
|
||||
@ -241,6 +244,11 @@ class RetransmissionQueue {
|
||||
size_t ssthresh_;
|
||||
// Partial Bytes Acked. See RFC4960.
|
||||
size_t partial_bytes_acked_;
|
||||
|
||||
// See `dcsctp::Metrics`.
|
||||
size_t rtx_packets_count_ = 0;
|
||||
uint64_t rtx_bytes_count_ = 0;
|
||||
|
||||
// If set, fast recovery is enabled until this TSN has been cumulative
|
||||
// acked.
|
||||
absl::optional<UnwrappedTSN> fast_recovery_exit_tsn_ = absl::nullopt;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user