Include stream information in idle timeout connection close detail. Protected by FLAGS_quic_reloadable_flag_quic_add_stream_info_to_idle_close_detail. PiperOrigin-RevId: 330939479 Change-Id: I79754a0ae790df319b3ff0d1d41a35ebb505f207
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index 96b46d2..6be7633 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -4815,15 +4815,23 @@ const QuicTime::Delta duration = clock_->ApproximateNow() - idle_network_detector_.last_network_activity_time(); - const std::string error_details = quiche::QuicheStrCat( + std::string error_details = quiche::QuicheStrCat( "No recent network activity after ", duration.ToDebuggingValue(), ". Timeout:", idle_network_detector_.idle_network_timeout().ToDebuggingValue()); QUIC_DVLOG(1) << ENDPOINT << error_details; - if ((sent_packet_manager_.GetConsecutiveTlpCount() > 0 || - sent_packet_manager_.GetConsecutiveRtoCount() > 0 || - sent_packet_manager_.GetConsecutivePtoCount() > 0 || - visitor_->ShouldKeepConnectionAlive())) { + const bool has_consecutive_pto = + sent_packet_manager_.GetConsecutiveTlpCount() > 0 || + sent_packet_manager_.GetConsecutiveRtoCount() > 0 || + sent_packet_manager_.GetConsecutivePtoCount() > 0; + if (has_consecutive_pto || visitor_->ShouldKeepConnectionAlive()) { + if (GetQuicReloadableFlag(quic_add_stream_info_to_idle_close_detail) && + !has_consecutive_pto) { + // Include stream information in error detail if there are open streams. + QUIC_RELOADABLE_FLAG_COUNT(quic_add_stream_info_to_idle_close_detail); + error_details = quiche::QuicheStrCat( + error_details, ", ", visitor_->GetStreamsInfoForLogging()); + } CloseConnection(QUIC_NETWORK_IDLE_TIMEOUT, error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return;
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h index 5e4bf82..77b32dc 100644 --- a/quic/core/quic_connection.h +++ b/quic/core/quic_connection.h
@@ -170,6 +170,9 @@ // transactions expecting a response. virtual bool ShouldKeepConnectionAlive() const = 0; + // Called to retrieve streams information for logging purpose. + virtual std::string GetStreamsInfoForLogging() const = 0; + // Called when a self address change is observed. Returns true if self address // change is allowed. virtual bool AllowSelfAddressChange() const = 0;
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 2662ab7..7de6702 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -5970,6 +5970,9 @@ // Indicate streams are still open. EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) .WillRepeatedly(Return(true)); + if (GetQuicReloadableFlag(quic_add_stream_info_to_idle_close_detail)) { + EXPECT_CALL(visitor_, GetStreamsInfoForLogging()).WillOnce(Return("")); + } // This time, we should time out and send a connection close due to the TLP. EXPECT_CALL(visitor_,
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc index 4a2c49b..462e073 100644 --- a/quic/core/quic_session.cc +++ b/quic/core/quic_session.cc
@@ -696,6 +696,34 @@ write_blocked_streams_.HasWriteBlockedDataStreams(); } +std::string QuicSession::GetStreamsInfoForLogging() const { + std::string info = quiche::QuicheStrCat( + "num_active_streams: ", GetNumActiveStreams(), + ", num_pending_streams: ", pending_streams_size(), + ", num_outgoing_draining_streams: ", num_outgoing_draining_streams(), + " "); + // Log info for up to 5 streams. + size_t i = 5; + for (const auto& it : stream_map_) { + if (it.second->is_static()) { + continue; + } + // Calculate the stream creation delay. + const QuicTime::Delta delay = + connection_->clock()->ApproximateNow() - it.second->creation_time(); + info = quiche::QuicheStrCat( + info, "{", it.second->id(), ":", delay.ToDebuggingValue(), ";", + it.second->stream_bytes_written(), ",", it.second->fin_sent(), ",", + it.second->HasBufferedData(), ",", it.second->fin_buffered(), ";", + it.second->stream_bytes_read(), ",", it.second->fin_received(), "}"); + --i; + if (i == 0) { + break; + } + } + return info; +} + bool QuicSession::HasPendingHandshake() const { if (QuicVersionUsesCryptoFrames(transport_version())) { return GetCryptoStream()->HasPendingCryptoRetransmission() ||
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h index e8f3c46..14a46dd 100644 --- a/quic/core/quic_session.h +++ b/quic/core/quic_session.h
@@ -123,6 +123,7 @@ void OnAckNeedsRetransmittableFrame() override; void SendPing() override; bool WillingAndAbleToWrite() const override; + std::string GetStreamsInfoForLogging() const override; void OnPathDegrading() override; void OnForwardProgressMadeAfterPathDegrading() override; bool AllowSelfAddressChange() const override;
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc index 5d64f3b..0271352 100644 --- a/quic/core/quic_stream.cc +++ b/quic/core/quic_stream.cc
@@ -361,6 +361,7 @@ session->IsIncomingStream(id_), session->version()) : type), + creation_time_(session->connection()->clock()->ApproximateNow()), perspective_(session->perspective()) { if (type_ == WRITE_UNIDIRECTIONAL) { fin_received_ = true;
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h index a5520bd..f0a5a71 100644 --- a/quic/core/quic_stream.h +++ b/quic/core/quic_stream.h
@@ -354,6 +354,10 @@ static spdy::SpdyStreamPrecedence CalculateDefaultPriority( const QuicSession* session); + QuicTime creation_time() const { return creation_time_; } + + bool fin_buffered() const { return fin_buffered_; } + protected: // Called when data of [offset, offset + data_length] is buffered in send // buffer. @@ -408,8 +412,6 @@ StreamDelegateInterface* stream_delegate() { return stream_delegate_; } - bool fin_buffered() const { return fin_buffered_; } - const QuicSession* session() const { return session_; } QuicSession* session() { return session_; } @@ -545,6 +547,9 @@ // write unidirectional. const StreamType type_; + // Creation time of this stream, as reported by the QuicClock. + const QuicTime creation_time_; + Perspective perspective_; };
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index 854fc23..c37ee4c 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -529,6 +529,7 @@ MOCK_METHOD(void, OnForwardProgressMadeAfterPathDegrading, (), (override)); MOCK_METHOD(bool, WillingAndAbleToWrite, (), (const, override)); MOCK_METHOD(bool, ShouldKeepConnectionAlive, (), (const, override)); + MOCK_METHOD(std::string, GetStreamsInfoForLogging, (), (const, override)); MOCK_METHOD(void, OnSuccessfulVersionNegotiation, (const ParsedQuicVersion& version),
diff --git a/quic/test_tools/simulator/quic_endpoint.h b/quic/test_tools/simulator/quic_endpoint.h index cb3c386..ff9b680 100644 --- a/quic/test_tools/simulator/quic_endpoint.h +++ b/quic/test_tools/simulator/quic_endpoint.h
@@ -59,6 +59,7 @@ bool WillingAndAbleToWrite() const override; bool ShouldKeepConnectionAlive() const override; + std::string GetStreamsInfoForLogging() const override { return ""; } void OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/) override {} void OnBlockedFrame(const QuicBlockedFrame& /*frame*/) override {} void OnRstStream(const QuicRstStreamFrame& /*frame*/) override {}