mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-11-10 21:22:20 +03:00
QLog changes to support ack receive timestamps
Summary: Add additional ACK_RECEIVE_TIMESTAMP fields in Read and Write AckFrame logs. Reviewed By: bschlinker Differential Revision: D37799091 fbshipit-source-id: fb1d6fbe9b3bcb5c0f8f8786a787730eaa699d1c
This commit is contained in:
committed by
Facebook GitHub Bot
parent
28ea0a998c
commit
b605e9b689
@@ -155,8 +155,13 @@ std::unique_ptr<QLogPacketEvent> BaseQLogger::createPacketEvent(
|
|||||||
}
|
}
|
||||||
case QuicFrame::Type::ReadAckFrame: {
|
case QuicFrame::Type::ReadAckFrame: {
|
||||||
const auto& frame = *quicFrame.asReadAckFrame();
|
const auto& frame = *quicFrame.asReadAckFrame();
|
||||||
event->frames.push_back(
|
event->frames.push_back(std::make_unique<ReadAckFrameLog>(
|
||||||
std::make_unique<ReadAckFrameLog>(frame.ackBlocks, frame.ackDelay));
|
frame.ackBlocks,
|
||||||
|
frame.ackDelay,
|
||||||
|
frame.frameType,
|
||||||
|
frame.maybeLatestRecvdPacketTime,
|
||||||
|
frame.maybeLatestRecvdPacketNum,
|
||||||
|
frame.recvdPacketsTimestampRanges));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QuicFrame::Type::ReadStreamFrame: {
|
case QuicFrame::Type::ReadStreamFrame: {
|
||||||
@@ -276,7 +281,12 @@ std::unique_ptr<QLogPacketEvent> BaseQLogger::createPacketEvent(
|
|||||||
case QuicWriteFrame::Type::WriteAckFrame: {
|
case QuicWriteFrame::Type::WriteAckFrame: {
|
||||||
const WriteAckFrame& frame = *quicFrame.asWriteAckFrame();
|
const WriteAckFrame& frame = *quicFrame.asWriteAckFrame();
|
||||||
event->frames.push_back(std::make_unique<WriteAckFrameLog>(
|
event->frames.push_back(std::make_unique<WriteAckFrameLog>(
|
||||||
frame.ackBlocks, frame.ackDelay));
|
frame.ackBlocks,
|
||||||
|
frame.ackDelay,
|
||||||
|
frame.frameType,
|
||||||
|
frame.maybeLatestRecvdPacketTime,
|
||||||
|
frame.maybeLatestRecvdPacketNum,
|
||||||
|
frame.recvdPacketsTimestampRanges));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QuicWriteFrame::Type::WriteStreamFrame: {
|
case QuicWriteFrame::Type::WriteStreamFrame: {
|
||||||
|
|||||||
@@ -212,13 +212,33 @@ folly::dynamic RetireConnectionIdFrameLog::toDynamic() const {
|
|||||||
folly::dynamic ReadAckFrameLog::toDynamic() const {
|
folly::dynamic ReadAckFrameLog::toDynamic() const {
|
||||||
folly::dynamic d = folly::dynamic::object();
|
folly::dynamic d = folly::dynamic::object();
|
||||||
folly::dynamic ackRangeDynamic = folly::dynamic::array();
|
folly::dynamic ackRangeDynamic = folly::dynamic::array();
|
||||||
|
folly::dynamic recvdPacketsTimestampRangesDynamic = folly::dynamic::array();
|
||||||
|
|
||||||
for (const auto& b : ackBlocks) {
|
for (const auto& b : ackBlocks) {
|
||||||
ackRangeDynamic.push_back(
|
ackRangeDynamic.push_back(
|
||||||
folly::dynamic::array(b.startPacket, b.endPacket));
|
folly::dynamic::array(b.startPacket, b.endPacket));
|
||||||
}
|
}
|
||||||
d["acked_ranges"] = ackRangeDynamic;
|
d["acked_ranges"] = ackRangeDynamic;
|
||||||
d["frame_type"] = toQlogString(FrameType::ACK);
|
d["frame_type"] = toQlogString(frameType);
|
||||||
|
if (frameType == FrameType::ACK_RECEIVE_TIMESTAMPS) {
|
||||||
|
if (maybeLatestRecvdPacketTime.has_value()) {
|
||||||
|
d["latest_recvd_packet_time"] =
|
||||||
|
maybeLatestRecvdPacketTime.value().count();
|
||||||
|
}
|
||||||
|
if (maybeLatestRecvdPacketNum.has_value()) {
|
||||||
|
d["latest_recvd_packet_num"] = maybeLatestRecvdPacketNum.value();
|
||||||
|
}
|
||||||
|
for (auto it = recvdPacketsTimestampRanges.cbegin();
|
||||||
|
it != recvdPacketsTimestampRanges.cend();
|
||||||
|
++it) {
|
||||||
|
recvdPacketsTimestampRangesDynamic.push_back(folly::dynamic::array(
|
||||||
|
it->gap,
|
||||||
|
it->timestamp_delta_count,
|
||||||
|
it->deltas.front(),
|
||||||
|
it->deltas.back()));
|
||||||
|
}
|
||||||
|
d["timestamp_ranges"] = recvdPacketsTimestampRangesDynamic;
|
||||||
|
}
|
||||||
d["ack_delay"] = ackDelay.count();
|
d["ack_delay"] = ackDelay.count();
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -226,12 +246,32 @@ folly::dynamic ReadAckFrameLog::toDynamic() const {
|
|||||||
folly::dynamic WriteAckFrameLog::toDynamic() const {
|
folly::dynamic WriteAckFrameLog::toDynamic() const {
|
||||||
folly::dynamic d = folly::dynamic::object();
|
folly::dynamic d = folly::dynamic::object();
|
||||||
folly::dynamic ackRangeDynamic = folly::dynamic::array();
|
folly::dynamic ackRangeDynamic = folly::dynamic::array();
|
||||||
|
folly::dynamic recvdPacketsTimestampRangesDynamic = folly::dynamic::array();
|
||||||
|
|
||||||
for (auto it = ackBlocks.cbegin(); it != ackBlocks.cend(); ++it) {
|
for (auto it = ackBlocks.cbegin(); it != ackBlocks.cend(); ++it) {
|
||||||
ackRangeDynamic.push_back(folly::dynamic::array(it->start, it->end));
|
ackRangeDynamic.push_back(folly::dynamic::array(it->start, it->end));
|
||||||
}
|
}
|
||||||
d["acked_ranges"] = ackRangeDynamic;
|
d["acked_ranges"] = ackRangeDynamic;
|
||||||
d["frame_type"] = toQlogString(FrameType::ACK);
|
d["frame_type"] = toQlogString(frameType);
|
||||||
|
if (frameType == FrameType::ACK_RECEIVE_TIMESTAMPS) {
|
||||||
|
if (maybeLatestRecvdPacketTime.has_value()) {
|
||||||
|
d["latest_recvd_packet_time"] =
|
||||||
|
maybeLatestRecvdPacketTime.value().count();
|
||||||
|
}
|
||||||
|
if (maybeLatestRecvdPacketNum.has_value()) {
|
||||||
|
d["latest_recvd_packet_num"] = maybeLatestRecvdPacketNum.value();
|
||||||
|
}
|
||||||
|
for (auto it = recvdPacketsTimestampRanges.cbegin();
|
||||||
|
it != recvdPacketsTimestampRanges.cend();
|
||||||
|
++it) {
|
||||||
|
recvdPacketsTimestampRangesDynamic.push_back(folly::dynamic::array(
|
||||||
|
it->gap,
|
||||||
|
it->timestamp_delta_count,
|
||||||
|
it->deltas.front(),
|
||||||
|
it->deltas.back()));
|
||||||
|
}
|
||||||
|
d["timestamp_ranges"] = recvdPacketsTimestampRangesDynamic;
|
||||||
|
}
|
||||||
d["ack_delay"] = ackDelay.count();
|
d["ack_delay"] = ackDelay.count();
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,11 +194,24 @@ class ReadAckFrameLog : public QLogFrame {
|
|||||||
public:
|
public:
|
||||||
ReadAckFrame::Vec ackBlocks;
|
ReadAckFrame::Vec ackBlocks;
|
||||||
std::chrono::microseconds ackDelay;
|
std::chrono::microseconds ackDelay;
|
||||||
|
FrameType frameType;
|
||||||
|
folly::Optional<std::chrono::microseconds> maybeLatestRecvdPacketTime;
|
||||||
|
folly::Optional<PacketNum> maybeLatestRecvdPacketNum;
|
||||||
|
RecvdPacketsTimestampsRangeVec recvdPacketsTimestampRanges;
|
||||||
ReadAckFrameLog(
|
ReadAckFrameLog(
|
||||||
const ReadAckFrame::Vec& ackBlocksIn,
|
const ReadAckFrame::Vec& ackBlocksIn,
|
||||||
std::chrono::microseconds ackDelayIn)
|
std::chrono::microseconds ackDelayIn,
|
||||||
: ackBlocks{ackBlocksIn}, ackDelay{ackDelayIn} {}
|
FrameType frameTypeIn = FrameType::ACK,
|
||||||
|
folly::Optional<std::chrono::microseconds> maybeLatestRecvdPacketTimeIn =
|
||||||
|
folly::none,
|
||||||
|
folly::Optional<PacketNum> maybeLatestRecvdPacketNumIn = folly::none,
|
||||||
|
RecvdPacketsTimestampsRangeVec recvdPacketsTimestampRangesIn = {})
|
||||||
|
: ackBlocks{ackBlocksIn},
|
||||||
|
ackDelay{ackDelayIn},
|
||||||
|
frameType{frameTypeIn},
|
||||||
|
maybeLatestRecvdPacketTime{maybeLatestRecvdPacketTimeIn},
|
||||||
|
maybeLatestRecvdPacketNum{maybeLatestRecvdPacketNumIn},
|
||||||
|
recvdPacketsTimestampRanges(recvdPacketsTimestampRangesIn) {}
|
||||||
~ReadAckFrameLog() override = default;
|
~ReadAckFrameLog() override = default;
|
||||||
FOLLY_NODISCARD folly::dynamic toDynamic() const override;
|
FOLLY_NODISCARD folly::dynamic toDynamic() const override;
|
||||||
};
|
};
|
||||||
@@ -207,11 +220,26 @@ class WriteAckFrameLog : public QLogFrame {
|
|||||||
public:
|
public:
|
||||||
WriteAckFrame::AckBlockVec ackBlocks;
|
WriteAckFrame::AckBlockVec ackBlocks;
|
||||||
std::chrono::microseconds ackDelay;
|
std::chrono::microseconds ackDelay;
|
||||||
|
FrameType frameType;
|
||||||
|
folly::Optional<std::chrono::microseconds> maybeLatestRecvdPacketTime;
|
||||||
|
folly::Optional<PacketNum> maybeLatestRecvdPacketNum;
|
||||||
|
RecvdPacketsTimestampsRangeVec recvdPacketsTimestampRanges;
|
||||||
|
|
||||||
WriteAckFrameLog(
|
WriteAckFrameLog(
|
||||||
const WriteAckFrame::AckBlockVec& ackBlocksIn,
|
const WriteAckFrame::AckBlockVec& ackBlocksIn,
|
||||||
std::chrono::microseconds ackDelayIn)
|
std::chrono::microseconds ackDelayIn,
|
||||||
: ackBlocks{ackBlocksIn}, ackDelay{ackDelayIn} {}
|
FrameType frameTypeIn = FrameType::ACK,
|
||||||
|
folly::Optional<std::chrono::microseconds> maybeLatestRecvdPacketTimeIn =
|
||||||
|
folly::none,
|
||||||
|
folly::Optional<PacketNum> maybeLatestRecvdPacketNumIn = folly::none,
|
||||||
|
RecvdPacketsTimestampsRangeVec recvdPacketsTimestampRangesIn = {})
|
||||||
|
: ackBlocks{ackBlocksIn},
|
||||||
|
ackDelay{ackDelayIn},
|
||||||
|
frameType{frameTypeIn},
|
||||||
|
|
||||||
|
maybeLatestRecvdPacketTime{maybeLatestRecvdPacketTimeIn},
|
||||||
|
maybeLatestRecvdPacketNum{maybeLatestRecvdPacketNumIn},
|
||||||
|
recvdPacketsTimestampRanges{recvdPacketsTimestampRangesIn} {}
|
||||||
~WriteAckFrameLog() override = default;
|
~WriteAckFrameLog() override = default;
|
||||||
FOLLY_NODISCARD folly::dynamic toDynamic() const override;
|
FOLLY_NODISCARD folly::dynamic toDynamic() const override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -721,6 +721,82 @@ TEST_F(QLoggerTest, AddingMultipleFrames) {
|
|||||||
EXPECT_EQ(expected, gotEvents);
|
EXPECT_EQ(expected, gotEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(QLoggerTest, AddingAckReceiveTimestampFrame) {
|
||||||
|
folly::dynamic expected = folly::parseJson(
|
||||||
|
R"([
|
||||||
|
[
|
||||||
|
"0",
|
||||||
|
"transport",
|
||||||
|
"packet_sent",
|
||||||
|
{
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"ack_delay": 111,
|
||||||
|
"acked_ranges": [
|
||||||
|
[
|
||||||
|
300,
|
||||||
|
400
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
200
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"frame_type": "ack_receive_timestamps",
|
||||||
|
"latest_recvd_packet_num": 400,
|
||||||
|
"latest_recvd_packet_time": 100000,
|
||||||
|
"timestamp_ranges": [
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fin": true,
|
||||||
|
"frame_type": "stream",
|
||||||
|
"stream_id": "10",
|
||||||
|
"length": 0,
|
||||||
|
"offset": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"header": {
|
||||||
|
"packet_number": 100,
|
||||||
|
"packet_size": 10
|
||||||
|
},
|
||||||
|
"packet_type": "initial"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
])");
|
||||||
|
|
||||||
|
FileQLogger q(VantagePoint::Client);
|
||||||
|
RegularQuicWritePacket packet =
|
||||||
|
createNewPacket(100, PacketNumberSpace::Initial);
|
||||||
|
|
||||||
|
WriteAckFrame ackFrame;
|
||||||
|
ackFrame.frameType = FrameType::ACK_RECEIVE_TIMESTAMPS;
|
||||||
|
ackFrame.ackDelay = 111us;
|
||||||
|
ackFrame.ackBlocks.emplace_back(300, 400);
|
||||||
|
ackFrame.ackBlocks.emplace_back(100, 200);
|
||||||
|
ackFrame.maybeLatestRecvdPacketNum = 400;
|
||||||
|
ackFrame.maybeLatestRecvdPacketTime = 100ms;
|
||||||
|
RecvdPacketsTimestampsRange recvdPacketsTimestampsRange = {
|
||||||
|
.gap = 1, .timestamp_delta_count = 1, .deltas = {1}};
|
||||||
|
ackFrame.recvdPacketsTimestampRanges = {recvdPacketsTimestampsRange};
|
||||||
|
WriteStreamFrame streamFrame(streamId, offset, len, fin);
|
||||||
|
|
||||||
|
packet.frames.emplace_back(std::move(ackFrame));
|
||||||
|
packet.frames.emplace_back(std::move(streamFrame));
|
||||||
|
|
||||||
|
q.addPacket(packet, 10);
|
||||||
|
folly::dynamic gotDynamic = q.toDynamic();
|
||||||
|
gotDynamic["traces"][0]["events"][0][0] = "0"; // hardcode reference time
|
||||||
|
folly::dynamic gotEvents = gotDynamic["traces"][0]["events"];
|
||||||
|
EXPECT_EQ(expected, gotEvents);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(QLoggerTest, ConnectionCloseFollyDynamic) {
|
TEST_F(QLoggerTest, ConnectionCloseFollyDynamic) {
|
||||||
folly::dynamic expected = folly::parseJson(
|
folly::dynamic expected = folly::parseJson(
|
||||||
R"([[
|
R"([[
|
||||||
@@ -1654,4 +1730,150 @@ TEST_F(QLoggerTest, PrettyDatagram) {
|
|||||||
EXPECT_EQ(expected, parsed);
|
EXPECT_EQ(expected, parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(QLoggerTest, ReadAckReceiveTimestampsFrame) {
|
||||||
|
folly::dynamic expected = folly::parseJson(
|
||||||
|
R"({
|
||||||
|
"description": "Converted from file",
|
||||||
|
"qlog_version": "draft-00",
|
||||||
|
"title": "mvfst qlog",
|
||||||
|
"traces": [
|
||||||
|
{
|
||||||
|
"common_fields": {
|
||||||
|
"dcid": "0101",
|
||||||
|
"protocol_type": "QUIC_HTTP3",
|
||||||
|
"reference_time": "0",
|
||||||
|
"scid": ""
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"time_offset": 0,
|
||||||
|
"time_units": "us"
|
||||||
|
},
|
||||||
|
"description": "Generated qlog from connection",
|
||||||
|
"event_fields": [
|
||||||
|
"relative_time",
|
||||||
|
"category",
|
||||||
|
"event",
|
||||||
|
"data"
|
||||||
|
],
|
||||||
|
"events": [
|
||||||
|
[
|
||||||
|
"31",
|
||||||
|
"transport",
|
||||||
|
"packet_received",
|
||||||
|
{
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"fin": true,
|
||||||
|
"frame_type": "stream",
|
||||||
|
"stream_id": "10",
|
||||||
|
"length": 0,
|
||||||
|
"offset": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ack_delay": 111,
|
||||||
|
"acked_ranges": [
|
||||||
|
[
|
||||||
|
300,
|
||||||
|
400
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
200
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"frame_type": "ack_receive_timestamps",
|
||||||
|
"latest_recvd_packet_num": 400,
|
||||||
|
"latest_recvd_packet_time": 100000,
|
||||||
|
"timestamp_ranges": [
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"header": {
|
||||||
|
"packet_number": 1,
|
||||||
|
"packet_size": 10
|
||||||
|
},
|
||||||
|
"packet_type": "1RTT"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"title": "mvfst qlog from single connection",
|
||||||
|
"vantage_point": {
|
||||||
|
"name": "server",
|
||||||
|
"type": "server"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": {
|
||||||
|
"max_duration": 0,
|
||||||
|
"total_event_count": 1,
|
||||||
|
"trace_count": 1
|
||||||
|
}
|
||||||
|
})");
|
||||||
|
|
||||||
|
auto headerIn =
|
||||||
|
ShortHeader(ProtectionType::KeyPhaseZero, getTestConnectionId(1), 1);
|
||||||
|
RegularQuicPacket regularQuicPacket(std::move(headerIn));
|
||||||
|
|
||||||
|
ReadAckFrame ackFrame;
|
||||||
|
ackFrame.frameType = FrameType::ACK_RECEIVE_TIMESTAMPS;
|
||||||
|
ackFrame.ackDelay = 111us;
|
||||||
|
ackFrame.ackBlocks.emplace_back(300, 400);
|
||||||
|
ackFrame.ackBlocks.emplace_back(100, 200);
|
||||||
|
ackFrame.maybeLatestRecvdPacketNum = 400;
|
||||||
|
ackFrame.maybeLatestRecvdPacketTime = 100ms;
|
||||||
|
RecvdPacketsTimestampsRange recvdPacketsTimestampsRange = {
|
||||||
|
.gap = 1, .timestamp_delta_count = 1, .deltas = {1}};
|
||||||
|
ackFrame.recvdPacketsTimestampRanges = {recvdPacketsTimestampsRange};
|
||||||
|
|
||||||
|
ReadStreamFrame frame(streamId, offset, fin);
|
||||||
|
|
||||||
|
regularQuicPacket.frames.emplace_back(std::move(frame));
|
||||||
|
regularQuicPacket.frames.emplace_back(std::move(ackFrame));
|
||||||
|
auto dir = folly::fs::temp_directory_path().string();
|
||||||
|
|
||||||
|
auto* q = new FileQLogger(
|
||||||
|
VantagePoint::Server,
|
||||||
|
kHTTP3ProtocolType,
|
||||||
|
dir,
|
||||||
|
true /* prettyJson */,
|
||||||
|
true /* streaming */);
|
||||||
|
|
||||||
|
folly::Random::DefaultGenerator rng;
|
||||||
|
rng.seed(
|
||||||
|
std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||||
|
|
||||||
|
q->setDcid(ConnectionId(std::vector<uint8_t>{
|
||||||
|
static_cast<uint8_t>(
|
||||||
|
folly::Random::rand32(0, std::numeric_limits<uint8_t>::max(), rng)),
|
||||||
|
static_cast<uint8_t>(
|
||||||
|
folly::Random::rand32(0, std::numeric_limits<uint8_t>::max(), rng)),
|
||||||
|
static_cast<uint8_t>(
|
||||||
|
folly::Random::rand32(0, std::numeric_limits<uint8_t>::max(), rng)),
|
||||||
|
static_cast<uint8_t>(
|
||||||
|
folly::Random::rand32(0, std::numeric_limits<uint8_t>::max(), rng)),
|
||||||
|
}));
|
||||||
|
q->addPacket(regularQuicPacket, 10);
|
||||||
|
EXPECT_EQ(q->logs.size(), 0);
|
||||||
|
|
||||||
|
std::string outputPath =
|
||||||
|
folly::to<std::string>(dir, "/", (q->dcid.value()).hex(), ".qlog");
|
||||||
|
delete q;
|
||||||
|
|
||||||
|
std::ifstream file(outputPath, std::ifstream::in);
|
||||||
|
std::string str(
|
||||||
|
(std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
folly::dynamic parsed = folly::parseJson(str);
|
||||||
|
|
||||||
|
parsed["traces"][0]["events"][0][0] = "31"; // hardcode reference time
|
||||||
|
parsed["traces"][0]["common_fields"]["dcid"] = "0101";
|
||||||
|
|
||||||
|
EXPECT_EQ(expected, parsed);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace quic::test
|
} // namespace quic::test
|
||||||
|
|||||||
Reference in New Issue
Block a user