• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/test_tools/packet_dropping_test_writer.h"
6 
7 #include "quiche/quic/platform/api/quic_logging.h"
8 
9 namespace quic {
10 namespace test {
11 
12 // Every dropped packet must be followed by this number of succesfully written
13 // packets. This is to avoid flaky test failures and timeouts, for example, in
14 // case both the client and the server drop every other packet (which is
15 // statistically possible even if drop percentage is less than 50%).
16 const int32_t kMinSuccesfulWritesAfterPacketLoss = 2;
17 
18 // An alarm that is scheduled if a blocked socket is simulated to indicate
19 // it's writable again.
20 class WriteUnblockedAlarm : public QuicAlarm::DelegateWithoutContext {
21  public:
WriteUnblockedAlarm(PacketDroppingTestWriter * writer)22   explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
23       : writer_(writer) {}
24 
OnAlarm()25   void OnAlarm() override {
26     QUIC_DLOG(INFO) << "Unblocking socket.";
27     writer_->OnCanWrite();
28   }
29 
30  private:
31   PacketDroppingTestWriter* writer_;
32 };
33 
34 // An alarm that is scheduled every time a new packet is to be written at a
35 // later point.
36 class DelayAlarm : public QuicAlarm::DelegateWithoutContext {
37  public:
DelayAlarm(PacketDroppingTestWriter * writer)38   explicit DelayAlarm(PacketDroppingTestWriter* writer) : writer_(writer) {}
39 
OnAlarm()40   void OnAlarm() override {
41     QuicTime new_deadline = writer_->ReleaseOldPackets();
42     if (new_deadline.IsInitialized()) {
43       writer_->SetDelayAlarm(new_deadline);
44     }
45   }
46 
47  private:
48   PacketDroppingTestWriter* writer_;
49 };
50 
PacketDroppingTestWriter()51 PacketDroppingTestWriter::PacketDroppingTestWriter()
52     : clock_(nullptr),
53       cur_buffer_size_(0),
54       num_calls_to_write_(0),
55       // Do not require any number of successful writes before the first dropped
56       // packet.
57       num_consecutive_succesful_writes_(kMinSuccesfulWritesAfterPacketLoss),
58       fake_packet_loss_percentage_(0),
59       fake_drop_first_n_packets_(0),
60       fake_blocked_socket_percentage_(0),
61       fake_packet_reorder_percentage_(0),
62       fake_packet_delay_(QuicTime::Delta::Zero()),
63       fake_bandwidth_(QuicBandwidth::Zero()),
64       buffer_size_(0) {
65   uint64_t seed = QuicRandom::GetInstance()->RandUint64();
66   QUIC_LOG(INFO) << "Seeding packet loss with " << seed;
67   simple_random_.set_seed(seed);
68 }
69 
~PacketDroppingTestWriter()70 PacketDroppingTestWriter::~PacketDroppingTestWriter() {
71   if (write_unblocked_alarm_ != nullptr) {
72     write_unblocked_alarm_->PermanentCancel();
73   }
74   if (delay_alarm_ != nullptr) {
75     delay_alarm_->PermanentCancel();
76   }
77 }
78 
Initialize(QuicConnectionHelperInterface * helper,QuicAlarmFactory * alarm_factory,std::unique_ptr<Delegate> on_can_write)79 void PacketDroppingTestWriter::Initialize(
80     QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
81     std::unique_ptr<Delegate> on_can_write) {
82   clock_ = helper->GetClock();
83   write_unblocked_alarm_.reset(
84       alarm_factory->CreateAlarm(new WriteUnblockedAlarm(this)));
85   delay_alarm_.reset(alarm_factory->CreateAlarm(new DelayAlarm(this)));
86   on_can_write_ = std::move(on_can_write);
87 }
88 
WritePacket(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,PerPacketOptions * options)89 WriteResult PacketDroppingTestWriter::WritePacket(
90     const char* buffer, size_t buf_len, const QuicIpAddress& self_address,
91     const QuicSocketAddress& peer_address, PerPacketOptions* options) {
92   ++num_calls_to_write_;
93   ReleaseOldPackets();
94 
95   QuicWriterMutexLock lock(&config_mutex_);
96   if (fake_drop_first_n_packets_ > 0 &&
97       num_calls_to_write_ <=
98           static_cast<uint64_t>(fake_drop_first_n_packets_)) {
99     QUIC_DVLOG(1) << "Dropping first " << fake_drop_first_n_packets_
100                   << " packets (packet number " << num_calls_to_write_ << ")";
101     num_consecutive_succesful_writes_ = 0;
102     return WriteResult(WRITE_STATUS_OK, buf_len);
103   }
104 
105   // Drop every packet at 100%, otherwise always succeed for at least
106   // kMinSuccesfulWritesAfterPacketLoss packets between two dropped ones.
107   if (fake_packet_loss_percentage_ == 100 ||
108       (fake_packet_loss_percentage_ > 0 &&
109        num_consecutive_succesful_writes_ >=
110            kMinSuccesfulWritesAfterPacketLoss &&
111        (simple_random_.RandUint64() % 100 <
112         static_cast<uint64_t>(fake_packet_loss_percentage_)))) {
113     QUIC_DVLOG(1) << "Dropping packet " << num_calls_to_write_;
114     num_consecutive_succesful_writes_ = 0;
115     return WriteResult(WRITE_STATUS_OK, buf_len);
116   } else {
117     ++num_consecutive_succesful_writes_;
118   }
119 
120   if (fake_blocked_socket_percentage_ > 0 &&
121       simple_random_.RandUint64() % 100 <
122           static_cast<uint64_t>(fake_blocked_socket_percentage_)) {
123     QUICHE_CHECK(on_can_write_ != nullptr);
124     QUIC_DVLOG(1) << "Blocking socket for packet " << num_calls_to_write_;
125     if (!write_unblocked_alarm_->IsSet()) {
126       // Set the alarm to fire immediately.
127       write_unblocked_alarm_->Set(clock_->ApproximateNow());
128     }
129 
130     // Dropping this packet on retry could result in PTO timeout,
131     // make sure to avoid this.
132     num_consecutive_succesful_writes_ = 0;
133 
134     return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
135   }
136 
137   if (!fake_packet_delay_.IsZero() || !fake_bandwidth_.IsZero()) {
138     if (buffer_size_ > 0 && buf_len + cur_buffer_size_ > buffer_size_) {
139       // Drop packets which do not fit into the buffer.
140       QUIC_DVLOG(1) << "Dropping packet because the buffer is full.";
141       return WriteResult(WRITE_STATUS_OK, buf_len);
142     }
143 
144     // Queue it to be sent.
145     QuicTime send_time = clock_->ApproximateNow() + fake_packet_delay_;
146     if (!fake_bandwidth_.IsZero()) {
147       // Calculate a time the bandwidth limit would impose.
148       QuicTime::Delta bandwidth_delay = QuicTime::Delta::FromMicroseconds(
149           (buf_len * kNumMicrosPerSecond) / fake_bandwidth_.ToBytesPerSecond());
150       send_time = delayed_packets_.empty()
151                       ? send_time + bandwidth_delay
152                       : delayed_packets_.back().send_time + bandwidth_delay;
153     }
154     std::unique_ptr<PerPacketOptions> delayed_options;
155     if (options != nullptr) {
156       delayed_options = options->Clone();
157     }
158     delayed_packets_.push_back(
159         DelayedWrite(buffer, buf_len, self_address, peer_address,
160                      std::move(delayed_options), send_time));
161     cur_buffer_size_ += buf_len;
162 
163     // Set the alarm if it's not yet set.
164     if (!delay_alarm_->IsSet()) {
165       delay_alarm_->Set(send_time);
166     }
167 
168     return WriteResult(WRITE_STATUS_OK, buf_len);
169   }
170 
171   return QuicPacketWriterWrapper::WritePacket(buffer, buf_len, self_address,
172                                               peer_address, options);
173 }
174 
IsWriteBlocked() const175 bool PacketDroppingTestWriter::IsWriteBlocked() const {
176   if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
177     return true;
178   }
179   return QuicPacketWriterWrapper::IsWriteBlocked();
180 }
181 
SetWritable()182 void PacketDroppingTestWriter::SetWritable() {
183   if (write_unblocked_alarm_ != nullptr && write_unblocked_alarm_->IsSet()) {
184     write_unblocked_alarm_->Cancel();
185   }
186   QuicPacketWriterWrapper::SetWritable();
187 }
188 
ReleaseNextPacket()189 QuicTime PacketDroppingTestWriter::ReleaseNextPacket() {
190   if (delayed_packets_.empty()) {
191     return QuicTime::Zero();
192   }
193   QuicReaderMutexLock lock(&config_mutex_);
194   auto iter = delayed_packets_.begin();
195   // Determine if we should re-order.
196   if (delayed_packets_.size() > 1 && fake_packet_reorder_percentage_ > 0 &&
197       simple_random_.RandUint64() % 100 <
198           static_cast<uint64_t>(fake_packet_reorder_percentage_)) {
199     QUIC_DLOG(INFO) << "Reordering packets.";
200     ++iter;
201     // Swap the send times when re-ordering packets.
202     delayed_packets_.begin()->send_time = iter->send_time;
203   }
204 
205   QUIC_DVLOG(1) << "Releasing packet.  " << (delayed_packets_.size() - 1)
206                 << " remaining.";
207   // Grab the next one off the queue and send it.
208   QuicPacketWriterWrapper::WritePacket(
209       iter->buffer.data(), iter->buffer.length(), iter->self_address,
210       iter->peer_address, iter->options.get());
211   QUICHE_DCHECK_GE(cur_buffer_size_, iter->buffer.length());
212   cur_buffer_size_ -= iter->buffer.length();
213   delayed_packets_.erase(iter);
214 
215   // If there are others, find the time for the next to be sent.
216   if (delayed_packets_.empty()) {
217     return QuicTime::Zero();
218   }
219   return delayed_packets_.begin()->send_time;
220 }
221 
ReleaseOldPackets()222 QuicTime PacketDroppingTestWriter::ReleaseOldPackets() {
223   while (!delayed_packets_.empty()) {
224     QuicTime next_send_time = delayed_packets_.front().send_time;
225     if (next_send_time > clock_->Now()) {
226       return next_send_time;
227     }
228     ReleaseNextPacket();
229   }
230   return QuicTime::Zero();
231 }
232 
SetDelayAlarm(QuicTime new_deadline)233 void PacketDroppingTestWriter::SetDelayAlarm(QuicTime new_deadline) {
234   delay_alarm_->Set(new_deadline);
235 }
236 
OnCanWrite()237 void PacketDroppingTestWriter::OnCanWrite() { on_can_write_->OnCanWrite(); }
238 
DelayedWrite(const char * buffer,size_t buf_len,const QuicIpAddress & self_address,const QuicSocketAddress & peer_address,std::unique_ptr<PerPacketOptions> options,QuicTime send_time)239 PacketDroppingTestWriter::DelayedWrite::DelayedWrite(
240     const char* buffer, size_t buf_len, const QuicIpAddress& self_address,
241     const QuicSocketAddress& peer_address,
242     std::unique_ptr<PerPacketOptions> options, QuicTime send_time)
243     : buffer(buffer, buf_len),
244       self_address(self_address),
245       peer_address(peer_address),
246       options(std::move(options)),
247       send_time(send_time) {}
248 
249 PacketDroppingTestWriter::DelayedWrite::~DelayedWrite() = default;
250 
251 }  // namespace test
252 }  // namespace quic
253