#include <pthread.h>
#include <signal.h>
-typedef struct
+typedef struct vtc_worker_ vcl_test_client_worker_t;
+typedef int (vtc_worker_run_fn) (vcl_test_client_worker_t *wrk);
+
+struct vtc_worker_
{
vcl_test_session_t *sessions;
vcl_test_session_t *qsessions;
uint32_t n_sessions;
- uint32_t n_qsessions;
uint32_t wrk_index;
fd_set wr_fdset;
fd_set rd_fdset;
int max_fd_index;
pthread_t thread_handle;
+ vtc_worker_run_fn *wrk_run_fn;
vcl_test_cfg_t cfg;
-} vcl_test_client_worker_t;
+};
typedef struct
{
vcl_test_client_worker_t *workers;
+ vcl_test_session_t ctrl_session;
vppcom_endpt_t server_endpt;
uint32_t cfg_seq_num;
- vcl_test_session_t ctrl_session;
- vcl_test_session_t *sessions;
uint8_t dump_cfg;
vcl_test_t post_test;
uint8_t proto;
+ uint8_t incremental_stats;
uint32_t n_workers;
volatile int active_workers;
volatile int test_running;
- struct sockaddr_storage server_addr;
+ union
+ {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } server_addr;
} vcl_test_client_main_t;
vcl_test_client_main_t vcl_client_main;
}
static int
-vtc_connect_test_sessions (vcl_test_client_worker_t * wrk)
+vtc_worker_alloc_sessions (vcl_test_client_worker_t *wrk)
{
- vcl_test_client_main_t *vcm = &vcl_client_main;
- vcl_test_main_t *vt = &vcl_test_main;
- const vcl_test_proto_vft_t *tp;
vcl_test_session_t *ts;
uint32_t n_test_sessions;
- int i, rv;
+ struct timespec now;
+ int i, j;
n_test_sessions = wrk->cfg.num_test_sessions;
if (n_test_sessions < 1)
return errno;
}
- tp = vt->protos[vcm->proto];
+ clock_gettime (CLOCK_REALTIME, &now);
for (i = 0; i < n_test_sessions; i++)
{
ts = &wrk->sessions[i];
+ memset (ts, 0, sizeof (*ts));
ts->session_index = i;
- rv = tp->open (&wrk->sessions[i], &vcm->server_endpt);
- if (rv < 0)
- return rv;
- }
- wrk->n_sessions = n_test_sessions;
-
-done:
- vtinf ("All test sessions (%d) connected!", n_test_sessions);
- return 0;
-}
-
-static int
-vtc_worker_test_setup (vcl_test_client_worker_t * wrk)
-{
- vcl_test_client_main_t *vcm = &vcl_client_main;
- vcl_test_session_t *ctrl = &vcm->ctrl_session;
- vcl_test_cfg_t *cfg = &wrk->cfg;
- vcl_test_session_t *ts;
- uint32_t sidx;
- int i, j;
-
- FD_ZERO (&wrk->wr_fdset);
- FD_ZERO (&wrk->rd_fdset);
-
- for (i = 0; i < cfg->num_test_sessions; i++)
- {
- ts = &wrk->sessions[i];
+ ts->old_stats.stop = now;
ts->cfg = wrk->cfg;
vcl_test_session_buf_alloc (ts);
- switch (cfg->test)
+ switch (ts->cfg.test)
{
- case VCL_TEST_TYPE_ECHO:
- memcpy (ts->txbuf, ctrl->txbuf, cfg->total_bytes);
- break;
case VCL_TEST_TYPE_UNI:
case VCL_TEST_TYPE_BI:
for (j = 0; j < ts->txbuf_size; j++)
ts->txbuf[j] = j & 0xff;
break;
+ default:
+ break;
}
-
- FD_SET (vppcom_session_index (ts->fd), &wrk->wr_fdset);
- FD_SET (vppcom_session_index (ts->fd), &wrk->rd_fdset);
- sidx = vppcom_session_index (ts->fd);
- wrk->max_fd_index = vtc_max (sidx, wrk->max_fd_index);
}
- wrk->max_fd_index += 1;
+ wrk->n_sessions = n_test_sessions;
+
+done:
+
+ vtinf ("All test sessions (%d) initialized!", n_test_sessions);
return 0;
}
vtc_worker_init (vcl_test_client_worker_t * wrk)
{
vcl_test_client_main_t *vcm = &vcl_client_main;
- vcl_test_cfg_t *cfg = &wrk->cfg;
- vcl_test_session_t *ts;
- uint32_t n;
int rv;
__wrk_index = wrk->wrk_index;
}
vt_atomic_add (&vcm->active_workers, 1);
}
- rv = vtc_connect_test_sessions (wrk);
+ rv = vtc_worker_alloc_sessions (wrk);
if (rv)
{
- vterr ("vtc_connect_test_sessions ()", rv);
+ vterr ("vtc_worker_alloc_sessions ()", rv);
return rv;
}
- if (vtc_worker_test_setup (wrk))
- return -1;
-
- for (n = 0; n < cfg->num_test_sessions; n++)
- {
- ts = &wrk->sessions[n];
- memset (&ts->stats, 0, sizeof (ts->stats));
- }
-
return 0;
}
{
ts = &wrk->sessions[i];
vppcom_session_close (ts->fd);
+ vcl_test_session_buf_free (ts);
}
wrk->n_sessions = 0;
}
-static void *
-vtc_worker_loop (void *arg)
+static void
+vtc_inc_stats_check (vcl_test_session_t *ts)
+{
+ /* Avoid checking time too often because of syscall cost */
+ if (ts->stats.tx_bytes - ts->old_stats.tx_bytes < 1 << 20)
+ return;
+
+ clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
+ if (vcl_test_time_diff (&ts->old_stats.stop, &ts->stats.stop) > 1)
+ {
+ vcl_test_stats_dump_inc (ts, 0 /* is_rx */);
+ ts->old_stats = ts->stats;
+ }
+}
+
+static void
+vtc_worker_start_transfer (vcl_test_client_worker_t *wrk)
+{
+ vtinf ("Worker %u starting transfer ...", wrk->wrk_index);
+
+ if (wrk->wrk_index == 0)
+ {
+ vcl_test_client_main_t *vcm = &vcl_client_main;
+ vcl_test_session_t *ctrl = &vcm->ctrl_session;
+
+ clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
+ }
+}
+
+static int
+vtc_worker_connect_sessions_select (vcl_test_client_worker_t *wrk)
+{
+ vcl_test_client_main_t *vcm = &vcl_client_main;
+ vcl_test_main_t *vt = &vcl_test_main;
+ const vcl_test_proto_vft_t *tp;
+ vcl_test_session_t *ts;
+ uint32_t sidx;
+ int i, rv;
+
+ tp = vt->protos[vcm->proto];
+
+ FD_ZERO (&wrk->wr_fdset);
+ FD_ZERO (&wrk->rd_fdset);
+
+ for (i = 0; i < wrk->cfg.num_test_sessions; i++)
+ {
+ ts = &wrk->sessions[i];
+
+ rv = tp->open (&wrk->sessions[i], &vcm->server_endpt);
+ if (rv < 0)
+ return rv;
+
+ FD_SET (vppcom_session_index (ts->fd), &wrk->wr_fdset);
+ FD_SET (vppcom_session_index (ts->fd), &wrk->rd_fdset);
+ sidx = vppcom_session_index (ts->fd);
+ wrk->max_fd_index = vtc_max (sidx, wrk->max_fd_index);
+ }
+ wrk->max_fd_index += 1;
+
+ vtinf ("All test sessions (%d) connected!", wrk->cfg.num_test_sessions);
+
+ return 0;
+}
+
+static int
+vtc_worker_run_select (vcl_test_client_worker_t *wrk)
{
vcl_test_client_main_t *vcm = &vcl_client_main;
- vcl_test_session_t *ctrl = &vcm->ctrl_session;
- vcl_test_client_worker_t *wrk = arg;
- uint32_t n_active_sessions;
fd_set _wfdset, *wfdset = &_wfdset;
fd_set _rfdset, *rfdset = &_rfdset;
+ uint32_t n_active_sessions;
vcl_test_session_t *ts;
int i, rv, check_rx = 0;
- rv = vtc_worker_init (wrk);
+ rv = vtc_worker_connect_sessions_select (wrk);
if (rv)
{
- vterr ("vtc_worker_init()", rv);
- return 0;
+ vterr ("vtc_worker_connect_sessions()", rv);
+ return rv;
}
- vtinf ("Starting test ...");
-
- if (wrk->wrk_index == 0)
- clock_gettime (CLOCK_REALTIME, &ctrl->stats.start);
-
check_rx = wrk->cfg.test != VCL_TEST_TYPE_UNI;
n_active_sessions = wrk->cfg.num_test_sessions;
+
+ vtc_worker_start_transfer (wrk);
+
while (n_active_sessions && vcm->test_running)
{
_wfdset = wrk->wr_fdset;
if (rv < 0)
{
vterr ("vppcom_select()", rv);
- goto exit;
+ break;
}
else if (rv == 0)
continue;
for (i = 0; i < wrk->cfg.num_test_sessions; i++)
{
ts = &wrk->sessions[i];
- if (!((ts->stats.stop.tv_sec == 0) &&
- (ts->stats.stop.tv_nsec == 0)))
+ if (ts->is_done)
continue;
if (FD_ISSET (vppcom_session_index (ts->fd), rfdset)
{
rv = ts->read (ts, ts->rxbuf, ts->rxbuf_size);
if (rv < 0)
- goto exit;
+ break;
}
if (FD_ISSET (vppcom_session_index (ts->fd), wfdset)
{
vtwrn ("vppcom_test_write (%d) failed -- aborting test",
ts->fd);
- goto exit;
+ break;
}
+ if (vcm->incremental_stats)
+ vtc_inc_stats_check (ts);
}
if ((!check_rx && ts->stats.tx_bytes >= ts->cfg.total_bytes)
|| (check_rx && ts->stats.rx_bytes >= ts->cfg.total_bytes))
{
clock_gettime (CLOCK_REALTIME, &ts->stats.stop);
+ ts->is_done = 1;
n_active_sessions--;
}
}
}
-exit:
+
+ return 0;
+}
+
+static inline int
+vtc_worker_run (vcl_test_client_worker_t *wrk)
+{
+ int rv;
+
+ vtinf ("Worker %u starting test ...", wrk->wrk_index);
+
+ rv = wrk->wrk_run_fn (wrk);
+
vtinf ("Worker %d done ...", wrk->wrk_index);
+
+ return rv;
+}
+
+static void *
+vtc_worker_loop (void *arg)
+{
+ vcl_test_client_main_t *vcm = &vcl_client_main;
+ vcl_test_session_t *ctrl = &vcm->ctrl_session;
+ vcl_test_client_worker_t *wrk = arg;
+
+ if (vtc_worker_init (wrk))
+ goto done;
+
+ if (vtc_worker_run (wrk))
+ goto done;
+
vtc_accumulate_stats (wrk, ctrl);
sleep (VCL_TEST_DELAY_DISCONNECT);
vtc_worker_sessions_exit (wrk);
+
+done:
+
if (wrk->wrk_index)
vt_atomic_add (&vcm->active_workers, -1);
+
return 0;
}
vtinf ("%s-directional Stream Test Starting!",
ctrl->cfg.test == VCL_TEST_TYPE_BI ? "Bi" : "Uni");
+ memset (&ctrl->stats, 0, sizeof (vcl_test_stats_t));
cfg->total_bytes = cfg->num_writes * cfg->txbuf_size;
cfg->ctrl_handle = ctrl->fd;
void
print_usage_and_exit (void)
{
- fprintf (stderr,
- "vcl_test_client [OPTIONS] <ipaddr> <port>\n"
- " OPTIONS\n"
- " -h Print this message and exit.\n"
- " -6 Use IPv6\n"
- " -c Print test config before test.\n"
- " -w <dir> Write test results to <dir>.\n"
- " -X Exit after running test.\n"
- " -p <proto> Use <proto> transport layer\n"
- " -D Use UDP transport layer\n"
- " -L Use TLS transport layer\n"
- " -E Run Echo test.\n"
- " -N <num-writes> Test Cfg: number of writes.\n"
- " -R <rxbuf-size> Test Cfg: rx buffer size.\n"
- " -T <txbuf-size> Test Cfg: tx buffer size.\n"
- " -U Run Uni-directional test.\n"
- " -B Run Bi-directional test.\n"
- " -V Verbose mode.\n"
- " -I <N> Use N sessions.\n"
- " -s <N> Use N sessions.\n"
- " -q <n> QUIC : use N Ssessions on top of n Qsessions\n");
+ fprintf (
+ stderr,
+ "vcl_test_client [OPTIONS] <ipaddr> <port>\n"
+ " OPTIONS\n"
+ " -h Print this message and exit.\n"
+ " -6 Use IPv6\n"
+ " -c Print test config before test.\n"
+ " -w <dir> Write test results to <dir>.\n"
+ " -X Exit after running test.\n"
+ " -p <proto> Use <proto> transport layer\n"
+ " -D Use UDP transport layer\n"
+ " -L Use TLS transport layer\n"
+ " -E Run Echo test.\n"
+ " -N <num-writes> Test Cfg: number of writes.\n"
+ " -R <rxbuf-size> Test Cfg: rx buffer size.\n"
+ " -T <txbuf-size> Test Cfg: tx buffer size.\n"
+ " -U Run Uni-directional test.\n"
+ " -B Run Bi-directional test.\n"
+ " -V Verbose mode.\n"
+ " -I <N> Use N sessions.\n"
+ " -s <N> Use N sessions.\n"
+ " -S Print incremental stats per session.\n"
+ " -q <n> QUIC : use N Ssessions on top of n Qsessions\n");
exit (1);
}
int c, v;
opterr = 0;
- while ((c = getopt (argc, argv, "chnp:w:XE:I:N:R:T:UBV6DLs:q:")) != -1)
+ while ((c = getopt (argc, argv, "chnp:w:xXE:I:N:R:T:UBV6DLs:q:S")) != -1)
switch (c)
{
case 'c':
vcm->post_test = VCL_TEST_TYPE_EXIT;
break;
+ case 'x':
+ vcm->post_test = VCL_TEST_TYPE_NONE;
+ break;
+
case 'E':
if (strlen (optarg) > ctrl->txbuf_size)
{
vcm->proto = VPPCOM_PROTO_TLS;
break;
+ case 'S':
+ vcm->incremental_stats = 1;
+ break;
+
case '?':
switch (optopt)
{
print_usage_and_exit ();
}
- if (argc < (optind + 2))
+ if (argc > (optind + 2))
{
- vtwrn ("Insufficient number of arguments!");
+ vtwrn ("Invalid number of arguments!");
print_usage_and_exit ();
}
memset (&vcm->server_addr, 0, sizeof (vcm->server_addr));
if (ctrl->cfg.address_ip6)
{
- struct sockaddr_in6 *sddr6 = (struct sockaddr_in6 *) &vcm->server_addr;
- sddr6->sin6_family = AF_INET6;
- inet_pton (AF_INET6, argv[optind++], &(sddr6->sin6_addr));
- sddr6->sin6_port = htons (atoi (argv[optind]));
+ struct in6_addr *in6 = &vcm->server_addr.v6;
+ inet_pton (AF_INET6, argv[optind++], in6);
vcm->server_endpt.is_ip4 = 0;
- vcm->server_endpt.ip = (uint8_t *) & sddr6->sin6_addr;
- vcm->server_endpt.port = (uint16_t) sddr6->sin6_port;
+ vcm->server_endpt.ip = (uint8_t *) in6;
}
else
{
- struct sockaddr_in *saddr4 = (struct sockaddr_in *) &vcm->server_addr;
- saddr4->sin_family = AF_INET;
- inet_pton (AF_INET, argv[optind++], &(saddr4->sin_addr));
- saddr4->sin_port = htons (atoi (argv[optind]));
+ struct in_addr *in4 = &vcm->server_addr.v4;
+ inet_pton (AF_INET, argv[optind++], in4);
vcm->server_endpt.is_ip4 = 1;
- vcm->server_endpt.ip = (uint8_t *) & saddr4->sin_addr;
- vcm->server_endpt.port = (uint16_t) saddr4->sin_port;
+ vcm->server_endpt.ip = (uint8_t *) in4;
}
+
+ if (argc == optind + 1)
+ vcm->server_endpt.port = htons (atoi (argv[optind]));
+ else
+ vcm->server_endpt.port = htons (VCL_TEST_SERVER_PORT);
}
static void
vcl_test_session_t *ctrl = &vcm->ctrl_session;
int verbose = ctrl->cfg.verbose;
+ /* Only clients exits, server can accept new connections */
+ if (vcm->post_test == VCL_TEST_TYPE_EXIT_CLIENT)
+ return;
+
ctrl->cfg.test = VCL_TEST_TYPE_EXIT;
vtinf ("(fd %d): Sending exit cfg to server...", ctrl->fd);
if (verbose)
}
}
+static void
+vtc_alloc_workers (vcl_test_client_main_t *vcm)
+{
+ vcl_test_main_t *vt = &vcl_test_main;
+
+ vcm->workers = calloc (vcm->n_workers, sizeof (vcl_test_client_worker_t));
+ vt->wrk = calloc (vcm->n_workers, sizeof (vcl_test_wrk_t));
+
+ for (int i = 0; i < vcm->n_workers; i++)
+ {
+ vcm->workers[i].wrk_run_fn = vtc_worker_run_select;
+ }
+}
+
int
main (int argc, char **argv)
{
int rv;
vcm->n_workers = 1;
+ vcm->post_test = VCL_TEST_TYPE_EXIT_CLIENT;
+
vcl_test_cfg_init (&ctrl->cfg);
+ vt_incercept_sigs ();
vcl_test_session_buf_alloc (ctrl);
vtc_process_opts (vcm, argc, argv);
- vt_incercept_sigs ();
- vcm->workers = calloc (vcm->n_workers, sizeof (vcl_test_client_worker_t));
- vt->wrk = calloc (vcm->n_workers, sizeof (vcl_test_wrk_t));
+ vtc_alloc_workers (vcm);
rv = vppcom_app_create ("vcl_test_client");
if (rv < 0)
/* Protos like tls/dtls/quic need init */
if (vt->protos[vcm->proto]->init)
- vt->protos[vcm->proto]->init (&ctrl->cfg);
+ {
+ rv = vt->protos[vcm->proto]->init (&ctrl->cfg);
+ if (rv)
+ vtfail ("client init failed", rv);
+ }
if ((rv = vtc_ctrl_session_init (vcm, ctrl)))
vtfail ("vppcom_session_create() ctrl session", rv);
switch (vcm->post_test)
{
case VCL_TEST_TYPE_EXIT:
+ case VCL_TEST_TYPE_EXIT_CLIENT:
switch (ctrl->cfg.test)
{
case VCL_TEST_TYPE_EXIT: