1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * This is not really a test: this program prints out cwnd histogram
4 * for visual inspection.
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#ifndef WIN32
11#include <sys/ioctl.h>
12#include <termios.h>
13#include <unistd.h>
14#else
15#include <getopt.h>
16#endif
17
18#include "lsquic_types.h"
19#include "lsquic_int_types.h"
20#include "lsquic_cong_ctl.h"
21#include "lsquic_packet_common.h"
22#include "lsquic_packet_out.h"
23#include "lsquic_cubic.h"
24
25static const struct cong_ctl_if *const cci = &lsquic_cong_cubic_if;
26
27#define MS(n) ((n) * 1000)  /* MS: Milliseconds */
28
29enum event { EV_ACK, EV_LOSS, EV_TIMEOUT, };
30
31static const char *const evstr[] = {
32    [EV_ACK]     = "ACK",
33    [EV_LOSS]    = "LOSS",
34    [EV_TIMEOUT] = "TIMEOUT",
35};
36
37struct rec
38{
39    enum event      event;
40    unsigned        cwnd;
41};
42
43#define REC(ev) do {                                    \
44    if (i >= n_recs_alloc)                              \
45    {                                                   \
46        if (n_recs_alloc)                               \
47            n_recs_alloc *= 2;                          \
48        else                                            \
49            n_recs_alloc = 20;                          \
50        recs = realloc(recs, n_recs_alloc *             \
51                                    sizeof(recs[0]));   \
52    }                                                   \
53    recs[i].event = ev;                                 \
54    recs[i].cwnd  = cci->cci_get_cwnd(&cubic);      \
55    if (max_cwnd < recs[i].cwnd)                        \
56        max_cwnd = recs[i].cwnd;                        \
57} while (0)
58
59int
60main (int argc, char **argv)
61{
62    int i, n, opt;
63    int n_recs_alloc = 0;
64    int app_limited = 0;
65    unsigned unit = 100;    /* Default to 100 ms */
66    unsigned rtt_ms = 10;   /* Default to 10 ms */
67    struct lsquic_cubic cubic;
68    struct rec *recs = NULL;
69    unsigned max_cwnd, width;
70    char *line;
71#ifndef WIN32
72    struct winsize winsize;
73#endif
74    enum cubic_flags flags;
75    struct lsquic_packet_out packet_out;
76
77    cci->cci_init(&cubic, 0, 0);
78    max_cwnd = 0;
79    i = 0;
80    memset(&packet_out, 0, sizeof(packet_out));
81
82    while (-1 != (opt = getopt(argc, argv, "s:u:r:f:l:A:L:T:")))
83    {
84        switch (opt)
85        {
86        case 's':
87            cubic.cu_ssthresh = atoi(optarg);
88            break;
89        case 'r':
90            rtt_ms = atoi(optarg);
91            break;
92        case 'f':
93            flags = atoi(optarg);
94            lsquic_cubic_set_flags(&cubic, flags);
95            break;
96        case 'l':
97            app_limited = atoi(optarg);
98            break;
99        case 'A':
100            n = i + atoi(optarg);
101            for ( ; i < n; ++i)
102            {
103                packet_out.po_sent = MS(unit * i) - MS(rtt_ms);
104                cci->cci_ack(&cubic, &packet_out, 1370, MS(unit * i), app_limited);
105                REC(EV_ACK);
106            }
107            break;
108        case 'L':
109            n = i + atoi(optarg);
110            for ( ; i < n; ++i)
111            {
112                cci->cci_loss(&cubic);
113                REC(EV_LOSS);
114            }
115            break;
116        case 'T':
117            n = i + atoi(optarg);
118            for ( ; i < n; ++i)
119            {
120                cci->cci_timeout(&cubic);
121                REC(EV_TIMEOUT);
122            }
123            break;
124        case 'u':
125            unit = atoi(optarg);
126            break;
127        default:
128            exit(1);
129        }
130    }
131
132#ifndef WIN32
133    if (isatty(STDIN_FILENO))
134    {
135        if (0 == ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize))
136            width = winsize.ws_col;
137        else
138        {
139            perror("ioctl");
140            width = 80;
141        }
142    }
143    else
144#endif
145        width = 80;
146
147    width -= 5 /* cwnd */ + 1 /* space */ + 1 /* event type */ +
148                                            1 /* space */ + 1 /* newline */;
149    line = malloc(width);
150    memset(line, '+', width);
151
152    for (n = i, i = 0; i < n; ++i)
153        printf("%c % 5d %.*s\n", *evstr[recs[i].event], recs[i].cwnd,
154            (int) ((float) recs[i].cwnd / max_cwnd * width), line);
155
156    free(recs);
157    free(line);
158
159    return 0;
160}
161