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 {}