1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Helper script to generate time sequence plot using gnuplot.
16 // Accepts the trace on stdin, and outputs the gnuplot-consumable time series
17 // file into stdout.
18
19 #include <iostream>
20
21 #include "absl/container/flat_hash_map.h"
22 #include "absl/container/flat_hash_set.h"
23 #include "absl/flags/flag.h"
24 #include "absl/flags/parse.h"
25 #include "quic_trace/quic_trace.pb.h"
26
27 ABSL_FLAG(std::string,
28 sequence,
29 "",
30 "Which time sequence to plot: send, ack or loss.");
31 ABSL_FLAG(bool,
32 filter_old_acks,
33 true,
34 "Do not show an ack for a packet if it was already previously "
35 "acknowledged");
36
37 namespace quic_trace {
38 namespace {
39
40 // Calculates the amount of actual data in the packet.
FrameDataInSentPacket(const Event & packet)41 size_t FrameDataInSentPacket(const Event& packet) {
42 if (packet.event_type() != PACKET_SENT) {
43 return 0;
44 }
45
46 size_t sent_in_packet = 0;
47 for (const Frame& frame : packet.frames()) {
48 if (frame.frame_type() != STREAM || !frame.has_stream_frame_info()) {
49 continue;
50 }
51 sent_in_packet += frame.stream_frame_info().length();
52 }
53 return sent_in_packet;
54 }
55
56 struct SentPacket {
57 // Offset of the stream data sent in the frame with respect to the beginning
58 // of the connection.
59 size_t offset;
60 // Size of frame data in the packet.
61 size_t size;
62 };
63 // Map of the sent packets, keyed by packet number.
64 using SentPacketMap = absl::flat_hash_map<uint64_t, SentPacket>;
65
PrintSentPacket(const SentPacketMap & packet_map,uint64_t packet_number,uint64_t time)66 void PrintSentPacket(const SentPacketMap& packet_map,
67 uint64_t packet_number,
68 uint64_t time) {
69 auto original_packet_it = packet_map.find(packet_number);
70 if (original_packet_it == packet_map.end()) {
71 return;
72 }
73
74 const SentPacket& original_packet = original_packet_it->second;
75
76 std::cout << time << " " << original_packet.offset << std::endl;
77 std::cout << time << " " << (original_packet.offset + original_packet.size)
78 << std::endl;
79 std::cout << std::endl;
80 }
81
PrintTimeSequence(std::istream * trace_source)82 void PrintTimeSequence(std::istream* trace_source) {
83 Trace trace;
84 trace.ParseFromIstream(trace_source);
85
86 size_t total_sent = 0;
87 SentPacketMap packet_map;
88 absl::flat_hash_set<uint64_t> already_acknowledged;
89 // In a single pass, compute |packet_map| and output the requested sequence.
90 for (const Event& event : trace.events()) {
91 // Track all sent packets and their offsets in the plot.
92 size_t sent_in_packet = FrameDataInSentPacket(event);
93 if (sent_in_packet != 0) {
94 packet_map.emplace(event.packet_number(),
95 SentPacket{total_sent, sent_in_packet});
96 }
97 total_sent += sent_in_packet;
98
99 // Output sent packets.
100 if (sent_in_packet != 0 && absl::GetFlag(FLAGS_sequence) == "send") {
101 std::cout << event.time_us() << " " << (total_sent - sent_in_packet)
102 << std::endl;
103 std::cout << event.time_us() << " " << total_sent << std::endl;
104 std::cout << std::endl;
105 }
106
107 // Output loss events.
108 if (event.event_type() == PACKET_LOST &&
109 absl::GetFlag(FLAGS_sequence) == "loss") {
110 PrintSentPacket(packet_map, event.packet_number(), event.time_us());
111 }
112
113 // Output acks.
114 if (event.event_type() == PACKET_RECEIVED) {
115 for (const Frame& frame : event.frames()) {
116 if (frame.frame_type() == ACK &&
117 absl::GetFlag(FLAGS_sequence) == "ack") {
118 for (const AckBlock& block : frame.ack_info().acked_packets()) {
119 for (uint64_t packet = block.first_packet();
120 packet <= block.last_packet(); packet++) {
121 if (absl::GetFlag(FLAGS_filter_old_acks)) {
122 if (already_acknowledged.count(packet) > 0) {
123 continue;
124 }
125 already_acknowledged.insert(packet);
126 }
127 PrintSentPacket(packet_map, packet, event.time_us());
128 }
129 }
130 }
131 }
132 }
133 }
134 }
135
136 } // namespace
137 } // namespace quic_trace
138
main(int argc,char * argv[])139 int main(int argc, char* argv[]) {
140 absl::ParseCommandLine(argc, argv);
141
142 if (absl::GetFlag(FLAGS_sequence) != "send" &&
143 absl::GetFlag(FLAGS_sequence) != "ack" &&
144 absl::GetFlag(FLAGS_sequence) != "loss") {
145 std::cerr << "The --sequence parameter has to be set to either 'send', "
146 "'ack' or 'loss'."
147 << std::endl;
148 return 1;
149 }
150
151 quic_trace::PrintTimeSequence(&std::cin);
152 return 0;
153 }
154