test_bw_sampler.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE. */
2/* Test adapted from Chromium bandwidth_sampler_test.cc */
3// Copyright 2016 The Chromium Authors. All rights reserved.
4
5#include <assert.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include <string.h>
9#include <sys/queue.h>
10
11#include "lsquic.h"
12#include "lsquic_int_types.h"
13#include "lsquic_hash.h"
14#include "lsquic_packet_common.h"
15#include "lsquic_packet_out.h"
16#include "lsquic_bw_sampler.h"
17#include "lsquic_conn.h"
18#include "lsquic_malo.h"
19
20
21/* Convert seconds to microseconds */
22#define sec(val) ((val) * 1000 * 1000)
23
24/* Convert milliseconds to lsquic_time_t, which is microseconds */
25#define ms(val) ((val) * 1000)
26
27/* Microseconds */
28#define us(val) (val)
29
30#define kRegularPacketSize 1280
31
32#define PacketsToBytes(count_) ((count_) * kRegularPacketSize)
33
34#define FromKBytesPerSecond(size_) (size_ * 8000)
35
36// Enforce divisibility for some of the tests:
37//      "kRegularPacketSize has to be five times divisible by 2"
38typedef char packet_size_has_to_be_five_times_divisible_by_2[
39                                (kRegularPacketSize & 31) == 0 ? 1 : -1];
40
41struct sampler_test
42{
43    struct bw_sampler   sampler;
44    lsquic_time_t       time;
45    uint64_t            bytes_in_flight;
46    struct lsquic_conn  conn;
47    struct malo        *malo_po;
48};
49
50
51static void
52sampler_test_init (struct sampler_test *stest)
53{
54    memset(stest, 0, sizeof(*stest));
55    stest->time = ms(1000);     /* Time must not be zero, or test breaks */
56    LSCONN_INITIALIZE(&stest->conn);
57    lsquic_bw_sampler_init(&stest->sampler, &stest->conn, QUIC_FTBIT_STREAM);
58    stest->malo_po = lsquic_malo_create(sizeof(struct lsquic_packet_out));
59    assert(stest->malo_po);
60}
61
62
63static void
64sampler_test_cleanup (struct sampler_test *stest)
65{
66    lsquic_bw_sampler_cleanup(&stest->sampler);
67    lsquic_malo_destroy(stest->malo_po);
68}
69
70
71static struct lsquic_packet_out *
72sampler_test_send_packet (struct sampler_test *stest, lsquic_packno_t packno,
73                                                                    bool retx)
74{
75    struct lsquic_packet_out *packet_out;
76
77    packet_out = lsquic_malo_get(stest->malo_po);
78    assert(packet_out);
79    memset(packet_out, 0, sizeof(*packet_out));
80    packet_out->po_packno = packno;
81    packet_out->po_flags |= PO_SENT_SZ;
82    packet_out->po_flags |= PO_HELLO;   /* Bypass sanity check */
83    packet_out->po_sent_sz = kRegularPacketSize;
84    packet_out->po_sent = stest->time;
85    if (retx)
86        packet_out->po_frame_types |= QUIC_FTBIT_STREAM;
87    lsquic_bw_sampler_packet_sent(&stest->sampler, packet_out,
88                                                    stest->bytes_in_flight);
89    if (retx)
90        stest->bytes_in_flight += packet_out->po_sent_sz;
91    return packet_out;
92}
93
94
95static struct bw_sample *
96sampler_test_ack_packet (struct sampler_test *stest,
97                                        struct lsquic_packet_out *packet_out)
98{
99    if (packet_out->po_frame_types & QUIC_FTBIT_STREAM)
100        stest->bytes_in_flight -= packet_out->po_sent_sz;
101    return lsquic_bw_sampler_packet_acked(&stest->sampler, packet_out,
102                                                                stest->time);
103}
104
105
106static void
107sampler_test_lose_packet (struct sampler_test *stest,
108                                        struct lsquic_packet_out *packet_out)
109{
110    if (packet_out->po_frame_types & QUIC_FTBIT_STREAM)
111        stest->bytes_in_flight -= packet_out->po_sent_sz;
112    lsquic_bw_sampler_packet_lost(&stest->sampler, packet_out);
113}
114
115
116static void
117sampler_test_send_40_packets_and_ack_first_20 (struct sampler_test *stest,
118        lsquic_time_t time_between_packets, struct lsquic_packet_out *packets[])
119{
120    struct bw_sample *sample;
121    unsigned i;
122
123    // Send 20 packets at a constant inter-packet time.
124    for (i = 1; i <= 20; i++)
125    {
126        packets[i] = sampler_test_send_packet(stest, i, true);
127        stest->time += time_between_packets;
128    }
129
130    // Ack packets 1 to 20, while sending new packets at the same rate as
131    // before.
132    for (i = 1; i <= 20; i++)
133    {
134        sample = sampler_test_ack_packet(stest, packets[i]);
135        assert(sample);
136        lsquic_malo_put(sample);
137        packets[i + 20] = sampler_test_send_packet(stest, i + 20, true);
138        stest->time += time_between_packets;
139    }
140}
141
142
143// Test the sampler in a simple stop-and-wait sender setting.
144static void
145test_send_and_wait (void)
146{
147    struct sampler_test stest;
148    lsquic_time_t time_between_packets = ms(10);
149    uint64_t expected_bandwidth = kRegularPacketSize * 100 * 8;
150    unsigned i;
151    struct bw_sample *sample;
152    struct lsquic_packet_out *packet;
153
154    sampler_test_init(&stest);
155
156    // Send packets at the constant bandwidth.
157    for (i = 1; i < 20; ++i)
158    {
159        packet = sampler_test_send_packet(&stest, i, true);
160        stest.time += time_between_packets;
161        sample = sampler_test_ack_packet(&stest, packet);
162        assert(sample);
163        assert(expected_bandwidth == BW_VALUE(&sample->bandwidth));
164        lsquic_malo_put(sample);
165    }
166
167    // Send packets at the exponentially decreasing bandwidth.
168    for (i = 20; i < 25; i++)
169    {
170        time_between_packets = time_between_packets * 2;
171        expected_bandwidth = expected_bandwidth / 2;
172        packet = sampler_test_send_packet(&stest, i, true);
173        stest.time += time_between_packets;
174        sample = sampler_test_ack_packet(&stest, packet);
175        assert(sample);
176        assert(expected_bandwidth == BW_VALUE(&sample->bandwidth));
177        lsquic_malo_put(sample);
178    }
179
180    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
181    assert(stest.bytes_in_flight == 0);
182
183    sampler_test_cleanup(&stest);
184}
185
186
187static void
188test_send_time_state (void)
189{
190    struct sampler_test stest;
191    lsquic_time_t time_between_packets = ms(10);
192    struct bw_sample *sample;
193    unsigned i;
194    struct lsquic_packet_out *packets[11];
195
196    sampler_test_init(&stest);
197
198    // Send packets 1-5.
199    for (i = 1; i <= 5; i++) {
200        packets[i] = sampler_test_send_packet(&stest, i, true);
201        assert(PacketsToBytes(i) == stest.sampler.bws_total_sent);
202        stest.time += time_between_packets;
203    }
204
205    /* The order of tests here is different.  Because the send state is
206     * deleted when packet is acked, we have to check its values first.
207     */
208#define SEND_STATE(idx_) (&packets[idx_]->po_bwp_state->bwps_send_state)
209
210    // Ack packet 1.
211    assert(PacketsToBytes(1) == SEND_STATE(1)->total_bytes_sent);
212    assert(0 == SEND_STATE(1)->total_bytes_acked);
213    assert(0 == SEND_STATE(1)->total_bytes_lost);
214    sample = sampler_test_ack_packet(&stest, packets[1]);
215    assert(sample);
216    lsquic_malo_put(sample);
217    assert(PacketsToBytes(1) == stest.sampler.bws_total_acked);
218
219    // Lose packet 2.
220    assert(PacketsToBytes(2) == SEND_STATE(2)->total_bytes_sent);
221    assert(0 == SEND_STATE(2)->total_bytes_acked);
222    assert(0 == SEND_STATE(2)->total_bytes_lost);
223    sampler_test_lose_packet(&stest, packets[2]);
224    assert(PacketsToBytes(1) == stest.sampler.bws_total_lost);
225
226    // Lose packet 3.
227    assert(PacketsToBytes(3) == SEND_STATE(3)->total_bytes_sent);
228    assert(0 == SEND_STATE(3)->total_bytes_acked);
229    assert(0 == SEND_STATE(3)->total_bytes_lost);
230    sampler_test_lose_packet(&stest, packets[3]);
231    assert(PacketsToBytes(2) == stest.sampler.bws_total_lost);
232
233    // Send packets 6-10.
234    for (i = 6; i <= 10; i++)
235    {
236        packets[i] = sampler_test_send_packet(&stest, i, true);
237        assert(PacketsToBytes(i) == stest.sampler.bws_total_sent);
238        stest.time += time_between_packets;
239    }
240
241    // Ack all inflight packets.
242    unsigned acked_packet_count = 1;
243    assert(PacketsToBytes(acked_packet_count) ==
244                                                stest.sampler.bws_total_acked);
245    for (i = 4; i <= 10; i++)
246    {
247        assert(PacketsToBytes(i) == SEND_STATE(i)->total_bytes_sent);
248        if (i <= 5)
249        {
250            assert(0 == SEND_STATE(i)->total_bytes_acked);
251            assert(0 == SEND_STATE(i)->total_bytes_lost);
252        }
253        else
254        {
255            assert(PacketsToBytes(1) == SEND_STATE(i)->total_bytes_acked);
256            assert(PacketsToBytes(2) == SEND_STATE(i)->total_bytes_lost);
257        }
258        sample = sampler_test_ack_packet(&stest, packets[i]);
259        assert(sample);
260        lsquic_malo_put(sample);
261        ++acked_packet_count;
262        assert(PacketsToBytes(acked_packet_count) ==
263                                                stest.sampler.bws_total_acked);
264        stest.time += time_between_packets;
265    }
266
267    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
268
269    sampler_test_cleanup(&stest);
270}
271
272
273// Test the sampler during regular windowed sender scenario with fixed
274// CWND of 20.
275static void
276test_send_paced (void)
277{
278    struct sampler_test stest;
279    const lsquic_time_t time_between_packets = ms(1);
280    uint64_t expected_bw = FromKBytesPerSecond(kRegularPacketSize);
281    unsigned i;
282    struct bw_sample *sample;
283    struct lsquic_packet_out *packets[41];
284
285    sampler_test_init(&stest);
286    sampler_test_send_40_packets_and_ack_first_20(&stest,
287                                                time_between_packets, packets);
288
289    // Ack the packets 21 to 40, arriving at the correct bandwidth.
290    for (i = 21; i <= 40; ++i)
291    {
292        sample = sampler_test_ack_packet(&stest, packets[i]);
293        assert(sample);
294        assert(expected_bw == BW_VALUE(&sample->bandwidth));
295        stest.time += time_between_packets;
296        lsquic_malo_put(sample);
297    }
298
299    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
300    assert(stest.bytes_in_flight == 0);
301
302    sampler_test_cleanup(&stest);
303}
304
305
306// Test the sampler in a scenario where 50% of packets is consistently lost.
307static void
308test_send_with_losses (void)
309{
310    struct sampler_test stest;
311    const lsquic_time_t time_between_packets = ms(1);
312    uint64_t expected_bw = FromKBytesPerSecond(kRegularPacketSize) / 2;
313    unsigned i;
314    struct bw_sample *sample;
315    struct lsquic_packet_out *packets[41];
316
317    sampler_test_init(&stest);
318
319    // Send 20 packets, each 1 ms apart.
320    for (i = 1; i <= 20; i++)
321    {
322        packets[i] = sampler_test_send_packet(&stest, i, true);
323        stest.time += time_between_packets;
324    }
325
326    // Ack packets 1 to 20, losing every even-numbered packet, while sending new
327    // packets at the same rate as before.
328    for (i = 1; i <= 20; i++)
329    {
330        if (i % 2 == 0)
331        {
332            sample = sampler_test_ack_packet(&stest, packets[i]);
333            assert(sample);
334            lsquic_malo_put(sample);
335        }
336        else
337            sampler_test_lose_packet(&stest, packets[i]);
338        packets[i + 20] = sampler_test_send_packet(&stest, i + 20, true);
339        stest.time += time_between_packets;
340    }
341
342    // Ack the packets 21 to 40 with the same loss pattern.
343    for (i = 21; i <= 40; i++)
344    {
345        if (i % 2 == 0)
346        {
347            sample = sampler_test_ack_packet(&stest, packets[i]);
348            assert(sample);
349            assert(expected_bw == BW_VALUE(&sample->bandwidth));
350            lsquic_malo_put(sample);
351        }
352        else
353            sampler_test_lose_packet(&stest, packets[i]);
354        stest.time += time_between_packets;
355    }
356
357    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
358    assert(stest.bytes_in_flight == 0);
359
360    sampler_test_cleanup(&stest);
361}
362
363
364// Test the sampler in a scenario where the 50% of packets are not
365// congestion controlled (specifically, non-retransmittable data is not
366// congestion controlled).  Should be functionally consistent in behavior with
367// the SendWithLosses test.
368static void
369test_not_congestion_controlled (void)
370{
371    struct sampler_test stest;
372    const lsquic_time_t time_between_packets = ms(1);
373    uint64_t expected_bw = FromKBytesPerSecond(kRegularPacketSize) / 2;
374    unsigned i;
375    struct bw_sample *sample;
376    struct lsquic_packet_out *packets[41];
377
378    sampler_test_init(&stest);
379
380    /* Note the mismatch between the comment and the code.  This is
381     * inherited from the original code.
382     */
383    // Send 20 packets, each 1 ms apart. Every even packet is not congestion
384    // controlled.
385    for (i = 1; i <= 20; i++)
386    {
387        packets[i] = sampler_test_send_packet(&stest, i,
388                                                i % 2 == 0 ? true : false);
389        stest.time += time_between_packets;
390    }
391
392    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 10);
393
394    // Ack packets 2 to 21, ignoring every even-numbered packet, while sending new
395    // packets at the same rate as before.
396    for (i = 1; i <= 20; i++)
397    {
398        if (i % 2 == 0)
399        {
400            sample = sampler_test_ack_packet(&stest, packets[i]);
401            assert(sample);
402            lsquic_malo_put(sample);
403        }
404        packets[i + 20] = sampler_test_send_packet(&stest, i + 20,
405                                                i % 2 == 0 ? true : false);
406        stest.time += time_between_packets;
407    }
408
409    // Ack the packets 22 to 41 with the same congestion controlled pattern.
410    for (i = 21; i <= 40; i++)
411    {
412        if (i % 2 == 0)
413        {
414            sample = sampler_test_ack_packet(&stest, packets[i]);
415            assert(sample);
416            assert(expected_bw == BW_VALUE(&sample->bandwidth));
417            lsquic_malo_put(sample);
418        }
419        stest.time += time_between_packets;
420    }
421
422    // Since only congestion controlled packets are entered into the map, it has
423    // to be empty at this point.
424    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
425    assert(stest.bytes_in_flight == 0);
426
427    sampler_test_cleanup(&stest);
428}
429
430
431// Simulate a situation where ACKs arrive in burst and earlier than usual, thus
432// producing an ACK rate which is higher than the original send rate.
433static void
434test_compressed_ack (void)
435{
436    struct sampler_test stest;
437    const lsquic_time_t time_between_packets = ms(1),
438                        ridiculously_small_time_delta = us(20);
439    uint64_t expected_bw = FromKBytesPerSecond(kRegularPacketSize);
440    uint64_t bw;
441    unsigned i;
442    struct bw_sample *sample;
443    struct lsquic_packet_out *packets[41];
444
445    sampler_test_init(&stest);
446    sampler_test_send_40_packets_and_ack_first_20(&stest,
447                                                time_between_packets, packets);
448
449    // Simulate an RTT somewhat lower than the one for 1-to-21 transmission.
450    stest.time += time_between_packets * 15;
451
452    // Ack the packets 21 to 40 almost immediately at once.
453    for (i = 21; i <= 40; i++)
454    {
455        sample = sampler_test_ack_packet(&stest, packets[i]);
456        assert(sample);
457        stest.time += ridiculously_small_time_delta;
458        bw = BW_VALUE(&sample->bandwidth);
459        lsquic_malo_put(sample);
460    }
461
462    assert(bw == expected_bw);
463    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
464    assert(stest.bytes_in_flight == 0);
465
466    sampler_test_cleanup(&stest);
467}
468
469
470// Tests receiving ACK packets in the reverse order.
471static void
472test_reordered_ack (void)
473{
474    struct sampler_test stest;
475    const lsquic_time_t time_between_packets = ms(1);
476    uint64_t expected_bw = FromKBytesPerSecond(kRegularPacketSize);
477    unsigned i;
478    struct bw_sample *sample;
479    struct lsquic_packet_out *packets[61];
480
481    sampler_test_init(&stest);
482    sampler_test_send_40_packets_and_ack_first_20(&stest,
483                                                time_between_packets, packets);
484
485    // Ack the packets 21 to 40 in the reverse order, while sending packets 41 to
486    // 60.
487    for (i = 0; i < 20; i++)
488    {
489        sample = sampler_test_ack_packet(&stest, packets[40 - i]);
490        assert(sample);
491        assert(expected_bw == BW_VALUE(&sample->bandwidth));
492        packets[41 + i] = sampler_test_send_packet(&stest, 41 + i, true);
493        stest.time += time_between_packets;
494        lsquic_malo_put(sample);
495    }
496
497    // Ack the packets 41 to 60, now in the regular order.
498    for (i = 41; i <= 60; i++)
499    {
500        sample = sampler_test_ack_packet(&stest, packets[i]);
501        assert(sample);
502        assert(expected_bw == BW_VALUE(&sample->bandwidth));
503        stest.time += time_between_packets;
504        lsquic_malo_put(sample);
505    }
506
507    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
508    assert(stest.bytes_in_flight == 0);
509
510    sampler_test_cleanup(&stest);
511}
512
513
514// Test the app-limited logic.
515static void
516test_app_limited (void)
517{
518    struct sampler_test stest;
519    const lsquic_time_t time_between_packets = ms(1);
520    uint64_t expected_bw = FromKBytesPerSecond(kRegularPacketSize);
521    unsigned i;
522    struct bw_sample *sample;
523    struct lsquic_packet_out *packets[81];
524
525    sampler_test_init(&stest);
526    sampler_test_send_40_packets_and_ack_first_20(&stest,
527                                                time_between_packets, packets);
528
529    // We are now app-limited. Ack 21 to 40 as usual, but do not send anything for
530    // now.
531    lsquic_bw_sampler_app_limited(&stest.sampler);
532    for (i = 21; i <= 40; i++)
533    {
534        sample = sampler_test_ack_packet(&stest, packets[i]);
535        assert(sample);
536        assert(expected_bw == BW_VALUE(&sample->bandwidth));
537        stest.time += time_between_packets;
538        lsquic_malo_put(sample);
539    }
540
541    stest.time += sec(1);
542
543    // Send packets 41 to 60, all of which would be marked as app-limited.
544    for (i = 41; i <= 60; i++)
545    {
546        packets[i] = sampler_test_send_packet(&stest, i, true);
547        stest.time += time_between_packets;
548    }
549
550    // Ack packets 41 to 60, while sending packets 61 to 80.  41 to 60 should be
551    // app-limited and underestimate the bandwidth due to that.
552    for (i = 41; i <= 60; i++)
553    {
554        sample = sampler_test_ack_packet(&stest, packets[i]);
555        assert(sample);
556        assert(sample->is_app_limited);
557        assert(BW_VALUE(&sample->bandwidth) < 0.7 * expected_bw);
558        packets[i + 20] = sampler_test_send_packet(&stest, i + 20, true);
559        stest.time += time_between_packets;
560        lsquic_malo_put(sample);
561    }
562
563    // Run out of packets, and then ack packet 61 to 80, all of which should have
564    // correct non-app-limited samples.
565    for (i = 61; i <= 80; i++)
566    {
567        sample = sampler_test_ack_packet(&stest, packets[i]);
568        assert(sample);
569        assert(BW_VALUE(&sample->bandwidth) == expected_bw);
570        stest.time += time_between_packets;
571        lsquic_malo_put(sample);
572    }
573
574    assert(lsquic_bw_sampler_entry_count(&stest.sampler) == 0);
575    assert(stest.bytes_in_flight == 0);
576
577    sampler_test_cleanup(&stest);
578}
579
580
581// Test the samples taken at the first flight of packets sent.
582static void
583test_first_round_trip (void)
584{
585    struct sampler_test stest;
586    const lsquic_time_t time_between_packets = ms(1),
587                        rtt = ms(800);
588    const unsigned num_packets = 10;
589    const uint64_t num_bytes = num_packets * kRegularPacketSize;
590    struct bandwidth real_bandwidth = BW_FROM_BYTES_AND_DELTA(num_bytes, rtt);
591    uint64_t last_bw;
592    unsigned i;
593    struct bw_sample *sample;
594    struct lsquic_packet_out *packets[11];
595
596    sampler_test_init(&stest);
597
598    for (i = 1; i <= 10; i++)
599    {
600        packets[i] = sampler_test_send_packet(&stest, i, true);
601        stest.time += time_between_packets;
602    }
603
604    stest.time += rtt - num_packets * time_between_packets;
605
606    last_bw = 0;
607    for (i = 1; i <= 10; i++)
608    {
609        sample = sampler_test_ack_packet(&stest, packets[i]);
610        assert(sample);
611        assert(BW_VALUE(&sample->bandwidth) > last_bw);
612        last_bw = BW_VALUE(&sample->bandwidth);
613        stest.time += time_between_packets;
614        lsquic_malo_put(sample);
615    }
616
617    // The final measured sample for the first flight of sample is expected to be
618    // smaller than the real bandwidth, yet it should not lose more than 10%. The
619    // specific value of the error depends on the difference between the RTT and
620    // the time it takes to exhaust the congestion window (i.e. in the limit when
621    // all packets are sent simultaneously, last sample would indicate the real
622    // bandwidth).
623    assert(last_bw < real_bandwidth.value);
624    assert(last_bw > 0.9f * real_bandwidth.value);
625
626    sampler_test_cleanup(&stest);
627}
628
629
630int
631main (void)
632{
633    test_send_and_wait();
634    test_send_time_state();
635    test_send_paced();
636    test_send_with_losses();
637    test_not_congestion_controlled();
638    test_compressed_ack();
639    test_reordered_ack();
640    test_app_limited();
641    test_first_round_trip();
642
643    return 0;
644}
645