xref: /aosp_15_r20/external/cronet/third_party/quic_trace/src/tools/quic_trace_to_time_sequence_gnuplot.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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