Populate stats needed for multi-port.

These stats will be logged by QuicConnectionLogger in Chrome.

PiperOrigin-RevId: 473031102
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index a3c0339..d359c28 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -681,6 +681,9 @@
   multi_port_enabled_ =
       connection_migration_use_new_cid_ &&
       config.HasClientSentConnectionOption(kMPQC, perspective_);
+  if (multi_port_enabled_) {
+    multi_port_stats_ = std::make_unique<MultiPortStats>();
+  }
 }
 
 void QuicConnection::EnableLegacyVersionEncapsulation(
@@ -6237,6 +6240,9 @@
 
 void QuicConnection::OnPathDegradingDetected() {
   is_path_degrading_ = true;
+  if (multi_port_stats_) {
+    multi_port_stats_->num_path_degrading++;
+  }
   visitor_->OnPathDegrading();
 }
 
@@ -6888,11 +6894,21 @@
   return true;
 }
 
-void QuicConnection::OnPathValidationFailureAtClient() {
+void QuicConnection::OnPathValidationFailureAtClient(bool is_multi_port) {
   if (connection_migration_use_new_cid_) {
     QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
     alternative_path_.Clear();
   }
+
+  if (is_multi_port && multi_port_stats_ != nullptr) {
+    if (is_path_degrading_) {
+      multi_port_stats_->num_multi_port_probe_failures_when_path_degrading++;
+    } else {
+      multi_port_stats_
+          ->num_multi_port_probe_failures_when_path_not_degrading++;
+    }
+  }
+
   RetirePeerIssuedConnectionIdsOnPathValidationFailure();
 }
 
@@ -7111,10 +7127,20 @@
 }
 
 void QuicConnection::OnMultiPortPathProbingSuccess(
-    std::unique_ptr<QuicPathValidationContext> context) {
+    std::unique_ptr<QuicPathValidationContext> context, QuicTime start_time) {
   multi_port_path_context_ = std::move(context);
   multi_port_probing_alarm_->Set(clock_->ApproximateNow() +
                                  multi_port_probing_interval_);
+  if (multi_port_stats_ != nullptr) {
+    auto now = clock_->Now();
+    auto time_delta = now - start_time;
+    multi_port_stats_->rtt_stats.UpdateRtt(time_delta, QuicTime::Delta::Zero(),
+                                           now);
+    if (is_path_degrading_) {
+      multi_port_stats_->rtt_stats_when_default_path_degrading.UpdateRtt(
+          time_delta, QuicTime::Delta::Zero(), now);
+    }
+  }
 }
 
 void QuicConnection::ProbeMultiPortPath() {
@@ -7141,14 +7167,14 @@
 
 void QuicConnection::MultiPortPathValidationResultDelegate::
     OnPathValidationSuccess(std::unique_ptr<QuicPathValidationContext> context,
-                            QuicTime /*start_time*/) {
-  connection_->OnMultiPortPathProbingSuccess(std::move(context));
+                            QuicTime start_time) {
+  connection_->OnMultiPortPathProbingSuccess(std::move(context), start_time);
 }
 
 void QuicConnection::MultiPortPathValidationResultDelegate::
     OnPathValidationFailure(
         std::unique_ptr<QuicPathValidationContext> /*context*/) {
-  connection_->OnPathValidationFailureAtClient();
+  connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/true);
 }
 
 QuicConnection::ReversePathValidationResultDelegate::
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index fd4c420..de52154 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -26,6 +26,7 @@
 
 #include "absl/strings/string_view.h"
 #include "absl/types/optional.h"
+#include "quiche/quic/core/congestion_control/rtt_stats.h"
 #include "quiche/quic/core/crypto/quic_decrypter.h"
 #include "quiche/quic/core/crypto/quic_encrypter.h"
 #include "quiche/quic/core/crypto/transport_parameters.h"
@@ -481,6 +482,19 @@
   QuicConnection& operator=(const QuicConnection&) = delete;
   ~QuicConnection() override;
 
+  struct MultiPortStats {
+    // general rtt stats of the multi-port path.
+    RttStats rtt_stats;
+    // rtt stats for the multi-port path when the default path is degrading.
+    RttStats rtt_stats_when_default_path_degrading;
+    // number of path degrading triggered when multi-port is enabled.
+    size_t num_path_degrading = 0;
+    // number of multi-port probe failures when path is not degrading
+    size_t num_multi_port_probe_failures_when_path_not_degrading = 0;
+    // number of multi-port probe failure when path is degrading
+    size_t num_multi_port_probe_failures_when_path_degrading = 0;
+  };
+
   // Sets connection parameters from the supplied |config|.
   void SetFromConfig(const QuicConfig& config);
 
@@ -748,7 +762,7 @@
   // Called in multi-port QUIC when the alternative path validation succeeds.
   // Stores the path validation context and prepares for the next validation.
   void OnMultiPortPathProbingSuccess(
-      std::unique_ptr<QuicPathValidationContext> context);
+      std::unique_ptr<QuicPathValidationContext> context, QuicTime start_time);
 
   // Probe the existing alternative path. Does not create a new alternative
   // path. This method is the callback for |multi_port_probing_alarm_|.
@@ -821,6 +835,10 @@
     multi_port_probing_interval_ = probing_interval;
   }
 
+  const MultiPortStats* multi_port_stats() const {
+    return multi_port_stats_.get();
+  }
+
   // Called when the ping alarm fires. Causes a ping frame to be sent only
   // if the retransmission alarm is not running.
   void OnPingTimeout();
@@ -1210,7 +1228,7 @@
 
   // Called to clear the alternative_path_ when path validation failed on the
   // client side.
-  void OnPathValidationFailureAtClient();
+  void OnPathValidationFailureAtClient(bool is_multi_port);
 
   void SetSourceAddressTokenToSend(absl::string_view token);
 
@@ -2286,6 +2304,8 @@
 
   QuicTime::Delta multi_port_probing_interval_;
 
+  std::unique_ptr<MultiPortStats> multi_port_stats_;
+
   RetransmittableOnWireBehavior retransmittable_on_wire_behavior_ = DEFAULT;
 
   bool only_send_probing_frames_on_alternative_path_ =
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index b2da24e..73300f7 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -2474,7 +2474,7 @@
     EXPECT_EQ(expected_self_address_, context->self_address());
     EXPECT_EQ(expected_peer_address_, context->peer_address());
     if (connection_->perspective() == Perspective::IS_CLIENT) {
-      connection_->OnPathValidationFailureAtClient();
+      connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false);
     }
     *success_ = false;
   }
@@ -13003,6 +13003,9 @@
   connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
   connection_.OnHandshakeComplete();
 
+  EXPECT_CALL(visitor_, OnPathDegrading());
+  connection_.OnPathDegradingDetected();
+
   auto self_address = connection_.self_address();
   const QuicSocketAddress kNewSelfAddress(self_address.host(),
                                           self_address.port() + 1);
@@ -13025,6 +13028,11 @@
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
       &connection_, kNewSelfAddress, connection_.peer_address()));
 
+  // 30ms RTT.
+  const QuicTime::Delta kTestRTT = QuicTime::Delta::FromMilliseconds(30);
+  // Fake a response delay.
+  clock_.AdvanceTime(kTestRTT);
+
   QuicFrames frames;
   frames.push_back(QuicFrame(QuicPathResponseFrame(
       99, new_writer.path_challenge_frames().front().data_buffer)));
@@ -13035,6 +13043,13 @@
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
       &connection_, kNewSelfAddress, connection_.peer_address()));
 
+  auto stats = connection_.multi_port_stats();
+  EXPECT_EQ(1, stats->num_path_degrading);
+  EXPECT_EQ(0, stats->num_multi_port_probe_failures_when_path_degrading);
+  EXPECT_EQ(kTestRTT, stats->rtt_stats.latest_rtt());
+  EXPECT_EQ(kTestRTT,
+            stats->rtt_stats_when_default_path_degrading.latest_rtt());
+
   connection_.GetMultiPortProbingAlarm()->Fire();
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -13050,6 +13065,9 @@
   EXPECT_FALSE(connection_.HasPendingPathValidation());
   EXPECT_FALSE(QuicConnectionPeer::IsAlternativePath(
       &connection_, kNewSelfAddress, connection_.peer_address()));
+  EXPECT_EQ(1, stats->num_path_degrading);
+  EXPECT_EQ(1, stats->num_multi_port_probe_failures_when_path_degrading);
+  EXPECT_EQ(0, stats->num_multi_port_probe_failures_when_path_not_degrading);
 }
 
 TEST_P(QuicConnectionTest, SingleAckInPacket) {
diff --git a/quiche/quic/tools/quic_client_base.cc b/quiche/quic/tools/quic_client_base.cc
index 766aa57..9277913 100644
--- a/quiche/quic/tools/quic_client_base.cc
+++ b/quiche/quic/tools/quic_client_base.cc
@@ -49,7 +49,8 @@
       std::unique_ptr<QuicPathValidationContext> context) override {
     QUIC_LOG(WARNING) << "Fail to validate path " << *context
                       << ", stop migrating.";
-    client_->session()->connection()->OnPathValidationFailureAtClient();
+    client_->session()->connection()->OnPathValidationFailureAtClient(
+        /*is_multi_port=*/false);
   }
 
  private:
@@ -464,7 +465,8 @@
       std::unique_ptr<QuicPathValidationContext> context) override {
     QUIC_LOG(WARNING) << "Fail to validate path " << *context
                       << ", stop migrating.";
-    client_->session()->connection()->OnPathValidationFailureAtClient();
+    client_->session()->connection()->OnPathValidationFailureAtClient(
+        /*is_multi_port=*/false);
   }
 
  private: