gfe-relnote: Add some bbr startup stats into QuicConnectionStats. Stats only. Not protected.

PiperOrigin-RevId: 241339065
Change-Id: Idef0c196eb0ab6bbe202a33d79e65a88a43678fd
diff --git a/quic/core/congestion_control/bbr_sender.cc b/quic/core/congestion_control/bbr_sender.cc
index a69dc33..e59aa8d 100644
--- a/quic/core/congestion_control/bbr_sender.cc
+++ b/quic/core/congestion_control/bbr_sender.cc
@@ -10,6 +10,7 @@
 
 #include "net/third_party/quiche/src/quic/core/congestion_control/rtt_stats.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
+#include "net/third_party/quiche/src/quic/core/quic_time.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_fallthrough.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
@@ -77,14 +78,17 @@
 
 BbrSender::DebugState::DebugState(const DebugState& state) = default;
 
-BbrSender::BbrSender(const RttStats* rtt_stats,
+BbrSender::BbrSender(QuicTime now,
+                     const RttStats* rtt_stats,
                      const QuicUnackedPacketMap* unacked_packets,
                      QuicPacketCount initial_tcp_congestion_window,
                      QuicPacketCount max_tcp_congestion_window,
-                     QuicRandom* random)
+                     QuicRandom* random,
+                     QuicConnectionStats* stats)
     : rtt_stats_(rtt_stats),
       unacked_packets_(unacked_packets),
       random_(random),
+      stats_(stats),
       mode_(STARTUP),
       round_trip_count_(0),
       max_bandwidth_(kBandwidthWindowSize, QuicBandwidth::Zero(), 0),
@@ -134,7 +138,11 @@
       probe_rtt_disabled_if_app_limited_(false),
       app_limited_since_last_probe_rtt_(false),
       min_rtt_since_last_probe_rtt_(QuicTime::Delta::Infinite()) {
-  EnterStartupMode();
+  if (stats_) {
+    stats_->slowstart_count = 0;
+    stats_->slowstart_start_time = QuicTime::Zero();
+  }
+  EnterStartupMode(now);
 }
 
 BbrSender::~BbrSender() {}
@@ -156,6 +164,11 @@
                              QuicPacketNumber packet_number,
                              QuicByteCount bytes,
                              HasRetransmittableData is_retransmittable) {
+  if (stats_ && InSlowStart()) {
+    ++stats_->slowstart_packets_sent;
+    stats_->slowstart_bytes_sent += bytes;
+  }
+
   last_sent_packet_ = packet_number;
 
   if (bytes_in_flight == 0 && sampler_.is_app_limited()) {
@@ -425,7 +438,12 @@
   return min_congestion_window_;
 }
 
-void BbrSender::EnterStartupMode() {
+void BbrSender::EnterStartupMode(QuicTime now) {
+  if (stats_) {
+    ++stats_->slowstart_count;
+    DCHECK_EQ(stats_->slowstart_start_time, QuicTime::Zero()) << mode_;
+    stats_->slowstart_start_time = now;
+  }
   mode_ = STARTUP;
   pacing_gain_ = high_gain_;
   congestion_window_gain_ = high_cwnd_gain_;
@@ -450,8 +468,14 @@
 void BbrSender::DiscardLostPackets(const LostPacketVector& lost_packets) {
   for (const LostPacket& packet : lost_packets) {
     sampler_.OnPacketLost(packet.packet_number);
-    if (startup_rate_reduction_multiplier_ != 0 && mode_ == STARTUP) {
-      startup_bytes_lost_ += packet.bytes_lost;
+    if (mode_ == STARTUP) {
+      if (stats_) {
+        ++stats_->slowstart_packets_lost;
+        stats_->slowstart_bytes_lost += packet.bytes_lost;
+      }
+      if (startup_rate_reduction_multiplier_ != 0) {
+        startup_bytes_lost_ += packet.bytes_lost;
+      }
     }
   }
 }
@@ -461,6 +485,9 @@
       last_acked_packet > current_round_trip_end_) {
     round_trip_count_++;
     current_round_trip_end_ = last_sent_packet_;
+    if (stats_ && InSlowStart()) {
+      ++stats_->slowstart_num_rtts;
+    }
     return true;
   }
 
@@ -603,6 +630,7 @@
 
 void BbrSender::MaybeExitStartupOrDrain(QuicTime now) {
   if (mode_ == STARTUP && is_at_full_bandwidth_) {
+    OnExitStartup(now);
     mode_ = DRAIN;
     pacing_gain_ = drain_gain_;
     congestion_window_gain_ = high_cwnd_gain_;
@@ -613,10 +641,25 @@
   }
 }
 
+void BbrSender::OnExitStartup(QuicTime now) {
+  DCHECK_EQ(mode_, STARTUP);
+  if (stats_) {
+    DCHECK_NE(stats_->slowstart_start_time, QuicTime::Zero());
+    if (now > stats_->slowstart_start_time) {
+      stats_->slowstart_duration =
+          now - stats_->slowstart_start_time + stats_->slowstart_duration;
+    }
+    stats_->slowstart_start_time = QuicTime::Zero();
+  }
+}
+
 void BbrSender::MaybeEnterOrExitProbeRtt(QuicTime now,
                                          bool is_round_start,
                                          bool min_rtt_expired) {
   if (min_rtt_expired && !exiting_quiescence_ && mode_ != PROBE_RTT) {
+    if (InSlowStart()) {
+      OnExitStartup(now);
+    }
     mode_ = PROBE_RTT;
     pacing_gain_ = 1;
     // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight|
@@ -644,7 +687,7 @@
       if (now >= exit_probe_rtt_at_ && probe_rtt_round_passed_) {
         min_rtt_timestamp_ = now;
         if (!is_at_full_bandwidth_) {
-          EnterStartupMode();
+          EnterStartupMode(now);
         } else {
           EnterProbeBandwidthMode(now);
         }
diff --git a/quic/core/congestion_control/bbr_sender.h b/quic/core/congestion_control/bbr_sender.h
index cae47f9..405300f 100644
--- a/quic/core/congestion_control/bbr_sender.h
+++ b/quic/core/congestion_control/bbr_sender.h
@@ -88,11 +88,13 @@
     QuicPacketNumber end_of_app_limited_phase;
   };
 
-  BbrSender(const RttStats* rtt_stats,
+  BbrSender(QuicTime now,
+            const RttStats* rtt_stats,
             const QuicUnackedPacketMap* unacked_packets,
             QuicPacketCount initial_tcp_congestion_window,
             QuicPacketCount max_tcp_congestion_window,
-            QuicRandom* random);
+            QuicRandom* random,
+            QuicConnectionStats* stats);
   BbrSender(const BbrSender&) = delete;
   BbrSender& operator=(const BbrSender&) = delete;
   ~BbrSender() override;
@@ -192,7 +194,7 @@
   bool ShouldExtendMinRttExpiry() const;
 
   // Enters the STARTUP mode.
-  void EnterStartupMode();
+  void EnterStartupMode(QuicTime now);
   // Enters the PROBE_BW mode.
   void EnterProbeBandwidthMode(QuicTime now);
 
@@ -236,7 +238,7 @@
   // Determines the appropriate congestion window for the connection.
   void CalculateCongestionWindow(QuicByteCount bytes_acked,
                                  QuicByteCount excess_acked);
-  // Determines the approriate window that constrains the in-flight during
+  // Determines the appropriate window that constrains the in-flight during
   // recovery.
   void CalculateRecoveryWindow(QuicByteCount bytes_acked,
                                QuicByteCount bytes_lost);
@@ -245,9 +247,13 @@
   // will be observed if present.
   bool IsPipeSufficientlyFull() const;
 
+  // Called right before exiting STARTUP.
+  void OnExitStartup(QuicTime now);
+
   const RttStats* rtt_stats_;
   const QuicUnackedPacketMap* unacked_packets_;
   QuicRandom* random_;
+  QuicConnectionStats* stats_;
 
   Mode mode_;
 
diff --git a/quic/core/congestion_control/bbr_sender_test.cc b/quic/core/congestion_control/bbr_sender_test.cc
index 205cf65..4de237f 100644
--- a/quic/core/congestion_control/bbr_sender_test.cc
+++ b/quic/core/congestion_control/bbr_sender_test.cc
@@ -23,6 +23,10 @@
 #include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
 #include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"
 
+using testing::AllOf;
+using testing::Ge;
+using testing::Le;
+
 namespace quic {
 namespace test {
 
@@ -127,11 +131,11 @@
         endpoint->connection()->sent_packet_manager().GetRttStats();
     // Ownership of the sender will be overtaken by the endpoint.
     BbrSender* sender = new BbrSender(
-        rtt_stats,
+        endpoint->connection()->clock()->Now(), rtt_stats,
         QuicSentPacketManagerPeer::GetUnackedPacketMap(
             QuicConnectionPeer::GetSentPacketManager(endpoint->connection())),
         kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets,
-        &random_);
+        &random_, QuicConnectionPeer::GetStats(endpoint->connection()));
     QuicConnectionPeer::SetSendAlgorithm(endpoint->connection(), sender);
     endpoint->RecordTrace();
     return sender;
@@ -367,11 +371,11 @@
   // Decrease the CWND gain so extra CWND is required with stretch acks.
   FLAGS_quic_bbr_cwnd_gain = 1.0;
   sender_ = new BbrSender(
-      rtt_stats_,
+      bbr_sender_.connection()->clock()->Now(), rtt_stats_,
       QuicSentPacketManagerPeer::GetUnackedPacketMap(
           QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())),
       kInitialCongestionWindowPackets, kDefaultMaxCongestionWindowPackets,
-      &random_);
+      &random_, QuicConnectionPeer::GetStats(bbr_sender_.connection()));
   QuicConnectionPeer::SetSendAlgorithm(bbr_sender_.connection(), sender_);
   // Enable Ack Decimation on the receiver.
   QuicConnectionPeer::SetAckMode(receiver_.connection(),
@@ -1310,5 +1314,27 @@
   EXPECT_GE(sender_->ExportDebugState().min_rtt_timestamp, probe_rtt_start);
 }
 
+TEST_F(BbrSenderTest, StartupStats) {
+  CreateDefaultSetup();
+
+  DriveOutOfStartup();
+  ASSERT_FALSE(sender_->InSlowStart());
+
+  const QuicConnectionStats& stats = bbr_sender_.connection()->GetStats();
+  EXPECT_EQ(1, stats.slowstart_count);
+  EXPECT_THAT(stats.slowstart_num_rtts, AllOf(Ge(5), Le(15)));
+  EXPECT_THAT(stats.slowstart_packets_sent, AllOf(Ge(100), Le(1000)));
+  EXPECT_THAT(stats.slowstart_bytes_sent, AllOf(Ge(1e5), Le(1e6)));
+  EXPECT_LE(stats.slowstart_packets_lost, 10);
+  EXPECT_LE(stats.slowstart_bytes_lost, 1e4);
+  EXPECT_THAT(stats.slowstart_duration,
+              AllOf(Ge(QuicTime::Delta::FromMilliseconds(500)),
+                    Le(QuicTime::Delta::FromMilliseconds(1500))));
+  EXPECT_EQ(QuicTime::Zero(), stats.slowstart_start_time);
+  EXPECT_EQ(stats.slowstart_duration,
+            QuicConnectionPeer::GetSentPacketManager(bbr_sender_.connection())
+                ->GetSlowStartDuration());
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/congestion_control/send_algorithm_interface.cc b/quic/core/congestion_control/send_algorithm_interface.cc
index 07efa43..0b3c6c8 100644
--- a/quic/core/congestion_control/send_algorithm_interface.cc
+++ b/quic/core/congestion_control/send_algorithm_interface.cc
@@ -30,9 +30,9 @@
   switch (congestion_control_type) {
     case kGoogCC:  // GoogCC is not supported by quic/core, fall back to BBR.
     case kBBR:
-      return new BbrSender(rtt_stats, unacked_packets,
+      return new BbrSender(clock->ApproximateNow(), rtt_stats, unacked_packets,
                            initial_congestion_window, max_congestion_window,
-                           random);
+                           random, stats);
     case kPCC:
       if (GetQuicReloadableFlag(quic_enable_pcc3)) {
         return CreatePccSender(clock, rtt_stats, unacked_packets, random, stats,
diff --git a/quic/core/quic_connection_stats.cc b/quic/core/quic_connection_stats.cc
index 5dea2ea..4d5c578 100644
--- a/quic/core/quic_connection_stats.cc
+++ b/quic/core/quic_connection_stats.cc
@@ -20,9 +20,14 @@
       bytes_spuriously_retransmitted(0),
       packets_spuriously_retransmitted(0),
       packets_lost(0),
+      slowstart_count(0),
+      slowstart_num_rtts(0),
       slowstart_packets_sent(0),
+      slowstart_bytes_sent(0),
       slowstart_packets_lost(0),
       slowstart_bytes_lost(0),
+      slowstart_duration(QuicTime::Delta::Zero()),
+      slowstart_start_time(QuicTime::Zero()),
       packets_dropped(0),
       crypto_retransmit_count(0),
       loss_timeout_count(0),
diff --git a/quic/core/quic_connection_stats.h b/quic/core/quic_connection_stats.h
index 8123fb0..df2f7f8 100644
--- a/quic/core/quic_connection_stats.h
+++ b/quic/core/quic_connection_stats.h
@@ -48,12 +48,22 @@
   // Number of packets abandoned as lost by the loss detection algorithm.
   QuicPacketCount packets_lost;
 
+  // Number of times this connection went through the slow start phase.
+  uint32_t slowstart_count;
+  // Number of round trips spent in slow start.
+  uint32_t slowstart_num_rtts;
   // Number of packets sent in slow start.
   QuicPacketCount slowstart_packets_sent;
+  // Number of bytes sent in slow start.
+  QuicByteCount slowstart_bytes_sent;
   // Number of packets lost exiting slow start.
   QuicPacketCount slowstart_packets_lost;
   // Number of bytes lost exiting slow start.
   QuicByteCount slowstart_bytes_lost;
+  // Time spent in COMPLETED slow start phases.
+  QuicTime::Delta slowstart_duration;
+  // Start time of the last slow start phase.
+  QuicTime slowstart_start_time;
 
   QuicPacketCount packets_dropped;  // Duplicate or less than least unacked.
   size_t crypto_retransmit_count;
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc
index 72c5c0f..89757de 100644
--- a/quic/core/quic_sent_packet_manager.cc
+++ b/quic/core/quic_sent_packet_manager.cc
@@ -13,6 +13,7 @@
 #include "net/third_party/quiche/src/quic/core/proto/cached_network_parameters.pb.h"
 #include "net/third_party/quiche/src/quic/core/quic_connection_stats.h"
 #include "net/third_party/quiche/src/quic/core/quic_pending_retransmission.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
@@ -1014,6 +1015,19 @@
   return retransmission_delay;
 }
 
+QuicTime::Delta QuicSentPacketManager::GetSlowStartDuration() const {
+  if (send_algorithm_->GetCongestionControlType() != kBBR) {
+    return QuicTime::Delta::Infinite();
+  }
+
+  if (!send_algorithm_->InSlowStart()) {
+    return stats_->slowstart_duration;
+  }
+
+  return clock_->ApproximateNow() - stats_->slowstart_start_time +
+         stats_->slowstart_duration;
+}
+
 std::string QuicSentPacketManager::GetDebugState() const {
   return send_algorithm_->GetDebugState();
 }
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h
index 4310014..c3c227e 100644
--- a/quic/core/quic_sent_packet_manager.h
+++ b/quic/core/quic_sent_packet_manager.h
@@ -235,6 +235,14 @@
     return send_algorithm_->GetSlowStartThreshold() / kDefaultTCPMSS;
   }
 
+  // Return the total time spent in slow start so far. If the sender is
+  // currently in slow start, the return value will include the duration between
+  // the most recent entry to slow start and now.
+  //
+  // Only implemented for BBR. Return QuicTime::Delta::Infinite() for other
+  // congestion controllers.
+  QuicTime::Delta GetSlowStartDuration() const;
+
   // Returns debugging information about the state of the congestion controller.
   std::string GetDebugState() const;
 
diff --git a/quic/core/quic_time.h b/quic/core/quic_time.h
index c8a1c84..cd33649 100644
--- a/quic/core/quic_time.h
+++ b/quic/core/quic_time.h
@@ -234,6 +234,12 @@
   return !(lhs < rhs);
 }
 
+// Override stream output operator for gtest or CHECK macros.
+inline std::ostream& operator<<(std::ostream& output, const QuicTime t) {
+  output << t.ToDebuggingValue();
+  return output;
+}
+
 // Non-member arithmetic operators for QuicTime::Delta.
 inline QuicTime::Delta operator+(QuicTime::Delta lhs, QuicTime::Delta rhs) {
   return QuicTime::Delta(lhs.time_offset_ + rhs.time_offset_);