Record pending duration histogram for a Quic stream if it was pending for whatever reason. And count how many streams has been pended for a QuicSession.

PiperOrigin-RevId: 589188268
diff --git a/quiche/quic/core/quic_connection_stats.h b/quiche/quic/core/quic_connection_stats.h
index 67a3884..d34b1fb 100644
--- a/quiche/quic/core/quic_connection_stats.h
+++ b/quiche/quic/core/quic_connection_stats.h
@@ -250,6 +250,9 @@
   std::optional<TlsServerOperationStats> tls_server_select_cert_stats;
   std::optional<TlsServerOperationStats> tls_server_compute_signature_stats;
   std::optional<TlsServerOperationStats> tls_server_decrypt_ticket_stats;
+
+  // The total number of streams which were pending from some time.
+  size_t num_total_pending_streams = 0;
 };
 
 }  // namespace quic
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index c59605e..fe48e83 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -194,6 +194,13 @@
     // The pending stream should now be in the scope of normal streams.
     QUICHE_DCHECK(IsClosedStream(stream_id) || IsOpenStream(stream_id))
         << "Stream " << stream_id << " not created";
+    if (!stream->pending_duration().IsZero()) {
+      QUIC_SERVER_HISTOGRAM_TIMES("QuicStream.PendingDurationUs",
+                                  stream->pending_duration().ToMicroseconds(),
+                                  0, 1000 * 100, 20,
+                                  "Time a stream has been pending at server.");
+      ++connection()->mutable_stats().num_total_pending_streams;
+    }
     pending_stream_map_.erase(stream_id);
     if (stop_sending_error_code) {
       stream->OnStopSending(*stop_sending_error_code);
diff --git a/quiche/quic/core/quic_session_test.cc b/quiche/quic/core/quic_session_test.cc
index 079db36..7539658 100644
--- a/quiche/quic/core/quic_session_test.cc
+++ b/quiche/quic/core/quic_session_test.cc
@@ -1940,12 +1940,20 @@
       QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id));
   EXPECT_EQ(0, session_.num_incoming_streams_created());
 
+  connection_->AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
   session_.ProcessAllPendingStreams();
   // Both bidirectional and read-unidirectional streams are unbuffered.
   EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id));
   EXPECT_FALSE(
       QuicSessionPeer::GetPendingStream(&session_, bidirectional_stream_id));
   EXPECT_EQ(2, session_.num_incoming_streams_created());
+  EXPECT_EQ(1, QuicSessionPeer::GetStream(&session_, stream_id)
+                   ->pending_duration()
+                   .ToMilliseconds());
+  EXPECT_EQ(1, QuicSessionPeer::GetStream(&session_, bidirectional_stream_id)
+                   ->pending_duration()
+                   .ToMilliseconds());
+  EXPECT_EQ(2, session_.connection()->GetStats().num_total_pending_streams);
 }
 
 TEST_P(QuicSessionTestServer, RstPendingStreams) {
diff --git a/quiche/quic/core/quic_stream.cc b/quiche/quic/core/quic_stream.cc
index 8bdf9d0..0da973d 100644
--- a/quiche/quic/core/quic_stream.cc
+++ b/quiche/quic/core/quic_stream.cc
@@ -126,7 +126,8 @@
                        kStreamReceiveWindowLimit,
                        session->flow_controller()->auto_tune_receive_window(),
                        session->flow_controller()),
-      sequencer_(this) {}
+      sequencer_(this),
+      creation_time_(session->GetClock()->ApproximateNow()) {}
 
 void PendingStream::OnDataAvailable() {
   // Data should be kept in the sequencer so that
@@ -284,14 +285,15 @@
 
 QuicStream::QuicStream(PendingStream* pending, QuicSession* session,
                        bool is_static)
-    : QuicStream(pending->id_, session, std::move(pending->sequencer_),
-                 is_static,
-                 QuicUtils::GetStreamType(pending->id_, session->perspective(),
-                                          /*peer_initiated = */ true,
-                                          session->version()),
-                 pending->stream_bytes_read_, pending->fin_received_,
-                 std::move(pending->flow_controller_),
-                 pending->connection_flow_controller_) {
+    : QuicStream(
+          pending->id_, session, std::move(pending->sequencer_), is_static,
+          QuicUtils::GetStreamType(pending->id_, session->perspective(),
+                                   /*peer_initiated = */ true,
+                                   session->version()),
+          pending->stream_bytes_read_, pending->fin_received_,
+          std::move(pending->flow_controller_),
+          pending->connection_flow_controller_,
+          (session->GetClock()->ApproximateNow() - pending->creation_time())) {
   QUICHE_DCHECK(session->version().HasIetfQuicFrames());
   sequencer_.set_stream(this);
 }
@@ -324,14 +326,15 @@
                        StreamType type)
     : QuicStream(id, session, QuicStreamSequencer(this), is_static, type, 0,
                  false, FlowController(id, session, type),
-                 session->flow_controller()) {}
+                 session->flow_controller(), QuicTime::Delta::Zero()) {}
 
 QuicStream::QuicStream(QuicStreamId id, QuicSession* session,
                        QuicStreamSequencer sequencer, bool is_static,
                        StreamType type, uint64_t stream_bytes_read,
                        bool fin_received,
                        std::optional<QuicFlowController> flow_controller,
-                       QuicFlowController* connection_flow_controller)
+                       QuicFlowController* connection_flow_controller,
+                       QuicTime::Delta pending_duration)
     : sequencer_(std::move(sequencer)),
       id_(id),
       session_(session),
@@ -369,6 +372,7 @@
                                            session->version())
                 : type),
       creation_time_(session->connection()->clock()->ApproximateNow()),
+      pending_duration_(pending_duration),
       perspective_(session->perspective()) {
   if (type_ == WRITE_UNIDIRECTIONAL) {
     fin_received_ = true;
diff --git a/quiche/quic/core/quic_stream.h b/quiche/quic/core/quic_stream.h
index 3633c3b..b7ea9e7 100644
--- a/quiche/quic/core/quic_stream.h
+++ b/quiche/quic/core/quic_stream.h
@@ -101,6 +101,8 @@
   // OnDataAvailable().
   void StopReading();
 
+  QuicTime creation_time() const { return creation_time_; }
+
  private:
   friend class QuicStream;
 
@@ -133,6 +135,8 @@
   QuicStreamSequencer sequencer_;
   // The error code received from QuicStopSendingFrame (if any).
   std::optional<QuicResetStreamError> stop_sending_error_code_;
+  // The time when this pending stream is created.
+  const QuicTime creation_time_;
 };
 
 class QUICHE_EXPORT QuicStream : public QuicStreamSequencer::StreamInterface {
@@ -394,6 +398,8 @@
   // level flow control window size.
   QuicByteCount CalculateSendWindowSize() const;
 
+  const QuicTime::Delta pending_duration() const { return pending_duration_; }
+
  protected:
   // Called when data of [offset, offset + data_length] is buffered in send
   // buffer.
@@ -490,7 +496,8 @@
              QuicStreamSequencer sequencer, bool is_static, StreamType type,
              uint64_t stream_bytes_read, bool fin_received,
              std::optional<QuicFlowController> flow_controller,
-             QuicFlowController* connection_flow_controller);
+             QuicFlowController* connection_flow_controller,
+             QuicTime::Delta pending_duration);
 
   // Calls MaybeSendBlocked on the stream's flow controller and the connection
   // level flow controller.  If the stream is flow control blocked by the
@@ -602,6 +609,10 @@
   // Creation time of this stream, as reported by the QuicClock.
   const QuicTime creation_time_;
 
+  // The duration when the data for this stream was stored in a PendingStream
+  // before being moved to this QuicStream.
+  const QuicTime::Delta pending_duration_;
+
   Perspective perspective_;
 };