blob: 5e152ea57d4baccf95493d44901f6ad0deba96fa [file] [log] [blame]
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/third_party/quiche/src/quic/core/quic_trace_visitor.h"
#include <string>
#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
namespace quic {
quic_trace::EncryptionLevel EncryptionLevelToProto(EncryptionLevel level) {
switch (level) {
case ENCRYPTION_INITIAL:
return quic_trace::ENCRYPTION_INITIAL;
case ENCRYPTION_HANDSHAKE:
return quic_trace::ENCRYPTION_HANDSHAKE;
case ENCRYPTION_ZERO_RTT:
return quic_trace::ENCRYPTION_0RTT;
case ENCRYPTION_FORWARD_SECURE:
return quic_trace::ENCRYPTION_1RTT;
case NUM_ENCRYPTION_LEVELS:
QUIC_BUG << "Invalid encryption level specified";
return quic_trace::ENCRYPTION_UNKNOWN;
}
}
QuicTraceVisitor::QuicTraceVisitor(const QuicConnection* connection)
: connection_(connection),
start_time_(connection_->clock()->ApproximateNow()) {
std::string binary_connection_id(connection->connection_id().data(),
connection->connection_id().length());
// We assume that the connection ID in gQUIC is equivalent to the
// server-chosen client-selected ID.
switch (connection->perspective()) {
case Perspective::IS_CLIENT:
trace_.set_destination_connection_id(binary_connection_id);
break;
case Perspective::IS_SERVER:
trace_.set_source_connection_id(binary_connection_id);
break;
}
}
void QuicTraceVisitor::OnPacketSent(const SerializedPacket& serialized_packet,
QuicPacketNumber /*original_packet_number*/,
TransmissionType /*transmission_type*/,
QuicTime sent_time) {
quic_trace::Event* event = trace_.add_events();
event->set_event_type(quic_trace::PACKET_SENT);
event->set_time_us(ConvertTimestampToRecordedFormat(sent_time));
event->set_packet_number(serialized_packet.packet_number.ToUint64());
event->set_packet_size(serialized_packet.encrypted_length);
event->set_encryption_level(
EncryptionLevelToProto(serialized_packet.encryption_level));
for (const QuicFrame& frame : serialized_packet.retransmittable_frames) {
switch (frame.type) {
case STREAM_FRAME:
case RST_STREAM_FRAME:
case CONNECTION_CLOSE_FRAME:
case WINDOW_UPDATE_FRAME:
case BLOCKED_FRAME:
case PING_FRAME:
PopulateFrameInfo(frame, event->add_frames());
break;
case PADDING_FRAME:
case MTU_DISCOVERY_FRAME:
case STOP_WAITING_FRAME:
case ACK_FRAME:
QUIC_BUG
<< "Frames of type are not retransmittable and are not supposed "
"to be in retransmittable_frames";
break;
// New IETF frames, not used in current gQUIC version.
case NEW_CONNECTION_ID_FRAME:
case RETIRE_CONNECTION_ID_FRAME:
case MAX_STREAMS_FRAME:
case STREAMS_BLOCKED_FRAME:
case PATH_RESPONSE_FRAME:
case PATH_CHALLENGE_FRAME:
case STOP_SENDING_FRAME:
case MESSAGE_FRAME:
case CRYPTO_FRAME:
case NEW_TOKEN_FRAME:
break;
// Ignore gQUIC-specific frames.
case GOAWAY_FRAME:
break;
case NUM_FRAME_TYPES:
QUIC_BUG << "Unknown frame type encountered";
break;
}
}
// Output PCC DebugState on packet sent for analysis.
if (connection_->sent_packet_manager()
.GetSendAlgorithm()
->GetCongestionControlType() == kPCC) {
PopulateTransportState(event->mutable_transport_state());
}
}
void QuicTraceVisitor::PopulateFrameInfo(const QuicFrame& frame,
quic_trace::Frame* frame_record) {
switch (frame.type) {
case STREAM_FRAME: {
frame_record->set_frame_type(quic_trace::STREAM);
quic_trace::StreamFrameInfo* info =
frame_record->mutable_stream_frame_info();
info->set_stream_id(frame.stream_frame.stream_id);
info->set_fin(frame.stream_frame.fin);
info->set_offset(frame.stream_frame.offset);
info->set_length(frame.stream_frame.data_length);
break;
}
case ACK_FRAME: {
frame_record->set_frame_type(quic_trace::ACK);
quic_trace::AckInfo* info = frame_record->mutable_ack_info();
info->set_ack_delay_us(frame.ack_frame->ack_delay_time.ToMicroseconds());
for (const auto& interval : frame.ack_frame->packets) {
quic_trace::AckBlock* block = info->add_acked_packets();
// We record intervals as [a, b], whereas the in-memory representation
// we currently use is [a, b).
block->set_first_packet(interval.min().ToUint64());
block->set_last_packet(interval.max().ToUint64() - 1);
}
break;
}
case RST_STREAM_FRAME: {
frame_record->set_frame_type(quic_trace::RESET_STREAM);
quic_trace::ResetStreamInfo* info =
frame_record->mutable_reset_stream_info();
info->set_stream_id(frame.rst_stream_frame->stream_id);
info->set_final_offset(frame.rst_stream_frame->byte_offset);
info->set_application_error_code(frame.rst_stream_frame->error_code);
break;
}
case CONNECTION_CLOSE_FRAME: {
frame_record->set_frame_type(quic_trace::CONNECTION_CLOSE);
quic_trace::CloseInfo* info = frame_record->mutable_close_info();
info->set_error_code(frame.connection_close_frame->quic_error_code);
info->set_reason_phrase(frame.connection_close_frame->error_details);
info->set_close_type(static_cast<quic_trace::CloseType>(
frame.connection_close_frame->close_type));
info->set_transport_close_frame_type(
frame.connection_close_frame->transport_close_frame_type);
break;
}
case GOAWAY_FRAME:
// Do not bother logging this since the frame in question is
// gQUIC-specific.
break;
case WINDOW_UPDATE_FRAME: {
bool is_connection = frame.window_update_frame->stream_id == 0;
frame_record->set_frame_type(is_connection ? quic_trace::MAX_DATA
: quic_trace::MAX_STREAM_DATA);
quic_trace::FlowControlInfo* info =
frame_record->mutable_flow_control_info();
info->set_max_data(frame.window_update_frame->byte_offset);
if (!is_connection) {
info->set_stream_id(frame.window_update_frame->stream_id);
}
break;
}
case BLOCKED_FRAME: {
bool is_connection = frame.blocked_frame->stream_id == 0;
frame_record->set_frame_type(is_connection ? quic_trace::BLOCKED
: quic_trace::STREAM_BLOCKED);
quic_trace::FlowControlInfo* info =
frame_record->mutable_flow_control_info();
if (!is_connection) {
info->set_stream_id(frame.window_update_frame->stream_id);
}
break;
}
case PING_FRAME:
case MTU_DISCOVERY_FRAME:
frame_record->set_frame_type(quic_trace::PING);
break;
case PADDING_FRAME:
frame_record->set_frame_type(quic_trace::PADDING);
break;
case STOP_WAITING_FRAME:
// We're going to pretend those do not exist.
break;
// New IETF frames, not used in current gQUIC version.
case NEW_CONNECTION_ID_FRAME:
case RETIRE_CONNECTION_ID_FRAME:
case MAX_STREAMS_FRAME:
case STREAMS_BLOCKED_FRAME:
case PATH_RESPONSE_FRAME:
case PATH_CHALLENGE_FRAME:
case STOP_SENDING_FRAME:
case MESSAGE_FRAME:
case CRYPTO_FRAME:
case NEW_TOKEN_FRAME:
break;
case NUM_FRAME_TYPES:
QUIC_BUG << "Unknown frame type encountered";
break;
}
}
void QuicTraceVisitor::OnIncomingAck(
const QuicAckFrame& ack_frame,
QuicTime ack_receive_time,
QuicPacketNumber /*largest_observed*/,
bool /*rtt_updated*/,
QuicPacketNumber /*least_unacked_sent_packet*/) {
quic_trace::Event* event = trace_.add_events();
event->set_time_us(ConvertTimestampToRecordedFormat(ack_receive_time));
event->set_packet_number(connection_->GetLargestReceivedPacket().ToUint64());
event->set_event_type(quic_trace::PACKET_RECEIVED);
// TODO(vasilvv): consider removing this copy.
QuicAckFrame copy_of_ack = ack_frame;
PopulateFrameInfo(QuicFrame(&copy_of_ack), event->add_frames());
PopulateTransportState(event->mutable_transport_state());
}
void QuicTraceVisitor::OnPacketLoss(QuicPacketNumber lost_packet_number,
TransmissionType transmission_type,
QuicTime detection_time) {
quic_trace::Event* event = trace_.add_events();
event->set_time_us(ConvertTimestampToRecordedFormat(detection_time));
event->set_event_type(quic_trace::PACKET_LOST);
event->set_packet_number(lost_packet_number.ToUint64());
PopulateTransportState(event->mutable_transport_state());
}
void QuicTraceVisitor::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
const QuicTime& receive_time) {
quic_trace::Event* event = trace_.add_events();
event->set_time_us(ConvertTimestampToRecordedFormat(receive_time));
event->set_event_type(quic_trace::PACKET_RECEIVED);
event->set_packet_number(connection_->GetLargestReceivedPacket().ToUint64());
// TODO(vasilvv): consider removing this copy.
QuicWindowUpdateFrame copy_of_update = frame;
PopulateFrameInfo(QuicFrame(&copy_of_update), event->add_frames());
}
void QuicTraceVisitor::OnSuccessfulVersionNegotiation(
const ParsedQuicVersion& version) {
uint32_t tag = QuicEndian::HostToNet32(CreateQuicVersionLabel(version));
std::string binary_tag(reinterpret_cast<const char*>(&tag), sizeof(tag));
trace_.set_protocol_version(binary_tag);
}
void QuicTraceVisitor::OnApplicationLimited() {
quic_trace::Event* event = trace_.add_events();
event->set_time_us(
ConvertTimestampToRecordedFormat(connection_->clock()->ApproximateNow()));
event->set_event_type(quic_trace::APPLICATION_LIMITED);
}
void QuicTraceVisitor::OnAdjustNetworkParameters(QuicBandwidth bandwidth,
QuicTime::Delta rtt) {
quic_trace::Event* event = trace_.add_events();
event->set_time_us(
ConvertTimestampToRecordedFormat(connection_->clock()->ApproximateNow()));
event->set_event_type(quic_trace::EXTERNAL_PARAMETERS);
quic_trace::ExternalNetworkParameters* parameters =
event->mutable_external_network_parameters();
if (!bandwidth.IsZero()) {
parameters->set_bandwidth_bps(bandwidth.ToBitsPerSecond());
}
if (!rtt.IsZero()) {
parameters->set_rtt_us(rtt.ToMicroseconds());
}
}
uint64_t QuicTraceVisitor::ConvertTimestampToRecordedFormat(
QuicTime timestamp) {
if (timestamp < start_time_) {
QUIC_BUG << "Timestamp went back in time while recording a trace";
return 0;
}
return (timestamp - start_time_).ToMicroseconds();
}
void QuicTraceVisitor::PopulateTransportState(
quic_trace::TransportState* state) {
const RttStats* rtt_stats = connection_->sent_packet_manager().GetRttStats();
state->set_min_rtt_us(rtt_stats->min_rtt().ToMicroseconds());
state->set_smoothed_rtt_us(rtt_stats->smoothed_rtt().ToMicroseconds());
state->set_last_rtt_us(rtt_stats->latest_rtt().ToMicroseconds());
state->set_cwnd_bytes(
connection_->sent_packet_manager().GetCongestionWindowInBytes());
QuicByteCount in_flight =
connection_->sent_packet_manager().GetBytesInFlight();
state->set_in_flight_bytes(in_flight);
state->set_pacing_rate_bps(connection_->sent_packet_manager()
.GetSendAlgorithm()
->PacingRate(in_flight)
.ToBitsPerSecond());
if (connection_->sent_packet_manager()
.GetSendAlgorithm()
->GetCongestionControlType() == kPCC) {
state->set_congestion_control_state(
connection_->sent_packet_manager().GetSendAlgorithm()->GetDebugState());
}
}
} // namespace quic