version 1.61, 2020/02/12 14:46:36 |
version 1.62, 2020/05/02 22:00:29 |
|
|
#include <fcntl.h> |
#include <fcntl.h> |
#include <poll.h> |
#include <poll.h> |
#include <paths.h> |
#include <paths.h> |
|
#include <math.h> |
|
|
#include <kvm.h> |
#include <kvm.h> |
#include <nlist.h> |
#include <nlist.h> |
|
|
u_long udp_slice_pkts; |
u_long udp_slice_pkts; |
}; |
}; |
|
|
|
struct statctx *udp_sc; /* singleton */ |
|
|
static void signal_handler(int, short, void *); |
static void signal_handler(int, short, void *); |
static void saddr_ntop(const struct sockaddr *, socklen_t, char *, size_t); |
static void saddr_ntop(const struct sockaddr *, socklen_t, char *, size_t); |
static void drop_gid(void); |
static void drop_gid(void); |
|
|
static void check_kvar(const char *); |
static void check_kvar(const char *); |
static char ** check_prepare_kvars(char *); |
static char ** check_prepare_kvars(char *); |
static void stats_prepare(struct statctx *); |
static void stats_prepare(struct statctx *); |
|
static void summary_display(void); |
static void tcp_stats_display(unsigned long long, long double, float, |
static void tcp_stats_display(unsigned long long, long double, float, |
struct statctx *, struct inpcb *, struct tcpcb *, struct socket *); |
struct statctx *, struct inpcb *, struct tcpcb *, struct socket *); |
static void tcp_process_slice(int, short, void *); |
static void tcp_process_slice(int, short, void *); |
static void tcp_server_handle_sc(int, short, void *); |
static void tcp_server_handle_sc(int, short, void *); |
static void tcp_server_accept(int, short, void *); |
static void tcp_server_accept(int, short, void *); |
static void server_init(struct addrinfo *, struct statctx *); |
static void server_init(struct addrinfo *); |
static void client_handle_sc(int, short, void *); |
static void client_handle_sc(int, short, void *); |
static void client_init(struct addrinfo *, int, struct statctx *, |
static void client_init(struct addrinfo *, int, struct addrinfo *); |
struct addrinfo *); |
|
static int clock_gettime_tv(clockid_t, struct timeval *); |
static int clock_gettime_tv(clockid_t, struct timeval *); |
static void udp_server_handle_sc(int, short, void *); |
static void udp_server_handle_sc(int, short, void *); |
static void udp_process_slice(int, short, void *); |
static void udp_process_slice(int, short, void *); |
static int map_tos(char *, int *); |
static int map_tos(char *, int *); |
|
static void quit(int, short, void *); |
|
static void wrapup(int); |
|
|
/* |
/* |
* We account the mainstats here, that is the stats |
* We account the mainstats here, that is the stats |
* for all connections, all variables starting with slice |
* for all connections, all variables starting with slice |
|
|
* between all slices so far. |
* between all slices so far. |
*/ |
*/ |
static struct { |
static struct { |
unsigned long long slice_bytes; /* bytes for last slice */ |
struct timeval t_first; /* first connect / packet */ |
|
unsigned long long total_bytes; /* bytes since t_first */ |
|
unsigned long long n_slices; /* slices since start */ |
|
unsigned long long slice_bytes; /* bytes since slice reset */ |
long double peak_mbps; /* peak mbps so far */ |
long double peak_mbps; /* peak mbps so far */ |
|
long double floor_mbps; /* floor mbps so far */ |
|
long double mean_mbps; /* mean mbps so far */ |
|
long double nvariance_mbps; /* for online std dev */ |
int nconns; /* connected clients */ |
int nconns; /* connected clients */ |
struct event timer; /* process timer */ |
struct event timer; /* process timer */ |
|
const char *host; /* remote server for display */ |
} mainstats; |
} mainstats; |
|
|
/* When adding variables, also add to tcp_stats_display() */ |
/* When adding variables, also add to tcp_stats_display() */ |
|
|
*/ |
*/ |
switch (sig) { |
switch (sig) { |
case SIGINT: |
case SIGINT: |
|
printf("\n"); |
|
wrapup(0); |
|
break; /* NOTREACHED */ |
case SIGTERM: |
case SIGTERM: |
case SIGHUP: |
case SIGHUP: |
warnx("Terminated by signal %d", sig); |
warnx("Terminated by signal %d", sig); |
exit(0); |
wrapup(0); |
break; /* NOTREACHED */ |
break; /* NOTREACHED */ |
default: |
default: |
errx(1, "unexpected signal %d", sig); |
errx(1, "unexpected signal %d", sig); |
|
|
if (clock_gettime_tv(CLOCK_MONOTONIC, &sc->t_start) == -1) |
if (clock_gettime_tv(CLOCK_MONOTONIC, &sc->t_start) == -1) |
err(1, "clock_gettime_tv"); |
err(1, "clock_gettime_tv"); |
sc->t_last = sc->t_start; |
sc->t_last = sc->t_start; |
|
if (!timerisset(&mainstats.t_first)) |
|
mainstats.t_first = sc->t_start; |
|
} |
|
|
|
static void |
|
summary_display(void) |
|
{ |
|
struct timeval t_cur, t_diff; |
|
long double std_dev; |
|
unsigned long long total_elapsed; |
|
char *direction; |
|
|
|
if (!ptb->sflag) { |
|
direction = "sent"; |
|
printf("--- %s tcpbench statistics ---\n", mainstats.host); |
|
} else { |
|
direction = "received"; |
|
printf("--- tcpbench server statistics ---\n"); |
|
} |
|
|
|
std_dev = sqrtl(mainstats.nvariance_mbps / mainstats.n_slices); |
|
|
|
if (clock_gettime_tv(CLOCK_MONOTONIC, &t_cur) == -1) |
|
err(1, "clock_gettime_tv"); |
|
timersub(&t_cur, &mainstats.t_first, &t_diff); |
|
total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000; |
|
|
|
printf("%llu bytes %s over %.3Lf seconds\n", |
|
mainstats.total_bytes, direction, total_elapsed/1000.0L); |
|
printf("bandwidth min/avg/max/std-dev = %.3Lf/%.3Lf/%.3Lf/%.3Lf Mbps\n", |
|
mainstats.floor_mbps, mainstats.mean_mbps, mainstats.peak_mbps, |
|
std_dev); |
} |
} |
|
|
static void |
static void |
|
|
tcp_process_slice(int fd, short event, void *bula) |
tcp_process_slice(int fd, short event, void *bula) |
{ |
{ |
unsigned long long total_elapsed, since_last; |
unsigned long long total_elapsed, since_last; |
long double mbps, slice_mbps = 0; |
long double mbps, old_mean_mbps, slice_mbps = 0; |
float bwperc; |
float bwperc; |
struct statctx *sc; |
struct statctx *sc; |
struct timeval t_cur, t_diff; |
struct timeval t_cur, t_diff; |
|
|
struct tcpcb tcpcb; |
struct tcpcb tcpcb; |
struct socket sockb; |
struct socket sockb; |
|
|
|
if (TAILQ_EMPTY(&sc_queue)) |
|
return; /* don't pollute stats */ |
|
|
|
mainstats.n_slices++; |
|
|
TAILQ_FOREACH(sc, &sc_queue, entry) { |
TAILQ_FOREACH(sc, &sc_queue, entry) { |
if (clock_gettime_tv(CLOCK_MONOTONIC, &t_cur) == -1) |
if (clock_gettime_tv(CLOCK_MONOTONIC, &t_cur) == -1) |
err(1, "clock_gettime_tv"); |
err(1, "clock_gettime_tv"); |
|
|
/* process stats for this slice */ |
/* process stats for this slice */ |
if (slice_mbps > mainstats.peak_mbps) |
if (slice_mbps > mainstats.peak_mbps) |
mainstats.peak_mbps = slice_mbps; |
mainstats.peak_mbps = slice_mbps; |
|
if (slice_mbps < mainstats.floor_mbps) |
|
mainstats.floor_mbps = slice_mbps; |
|
old_mean_mbps = mainstats.mean_mbps; |
|
mainstats.mean_mbps += (slice_mbps - mainstats.mean_mbps) / |
|
mainstats.n_slices; |
|
|
|
/* "Welford's method" for online variance |
|
* see Knuth, TAoCP Volume 2, 3rd edn., p232 */ |
|
mainstats.nvariance_mbps += (slice_mbps - old_mean_mbps) * |
|
(slice_mbps - mainstats.mean_mbps); |
|
|
printf("Conn: %3d Mbps: %12.3Lf Peak Mbps: %12.3Lf Avg Mbps: %12.3Lf\n", |
printf("Conn: %3d Mbps: %12.3Lf Peak Mbps: %12.3Lf Avg Mbps: %12.3Lf\n", |
mainstats.nconns, slice_mbps, mainstats.peak_mbps, |
mainstats.nconns, slice_mbps, mainstats.peak_mbps, |
mainstats.nconns ? slice_mbps / mainstats.nconns : 0); |
mainstats.nconns ? slice_mbps / mainstats.nconns : 0); |
mainstats.slice_bytes = 0; |
|
|
|
|
mainstats.slice_bytes = 0; |
set_slice_timer(mainstats.nconns > 0); |
set_slice_timer(mainstats.nconns > 0); |
} |
} |
|
|
static void |
static void |
udp_process_slice(int fd, short event, void *v_sc) |
udp_process_slice(int fd, short event, void *bula) |
{ |
{ |
struct statctx *sc = v_sc; |
|
unsigned long long total_elapsed, since_last, pps; |
unsigned long long total_elapsed, since_last, pps; |
long double slice_mbps; |
long double old_mean_mbps, slice_mbps; |
struct timeval t_cur, t_diff; |
struct timeval t_cur, t_diff; |
|
|
|
mainstats.n_slices++; |
|
|
if (clock_gettime_tv(CLOCK_MONOTONIC, &t_cur) == -1) |
if (clock_gettime_tv(CLOCK_MONOTONIC, &t_cur) == -1) |
err(1, "clock_gettime_tv"); |
err(1, "clock_gettime_tv"); |
/* Calculate pps */ |
|
timersub(&t_cur, &sc->t_start, &t_diff); |
timersub(&t_cur, &udp_sc->t_start, &t_diff); |
total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000; |
total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000; |
timersub(&t_cur, &sc->t_last, &t_diff); |
|
|
timersub(&t_cur, &udp_sc->t_last, &t_diff); |
since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000; |
since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000; |
if (since_last == 0) |
if (since_last == 0) |
return; |
return; |
slice_mbps = (sc->bytes * 8) / (since_last * 1000.0); |
|
pps = (sc->udp_slice_pkts * 1000) / since_last; |
slice_mbps = (udp_sc->bytes * 8) / (since_last * 1000.0); |
|
pps = (udp_sc->udp_slice_pkts * 1000) / since_last; |
|
|
if (slice_mbps > mainstats.peak_mbps) |
if (slice_mbps > mainstats.peak_mbps) |
mainstats.peak_mbps = slice_mbps; |
mainstats.peak_mbps = slice_mbps; |
|
if (slice_mbps < mainstats.floor_mbps) |
|
mainstats.floor_mbps = slice_mbps; |
|
old_mean_mbps = mainstats.mean_mbps; |
|
mainstats.mean_mbps += (slice_mbps - mainstats.mean_mbps) / |
|
mainstats.n_slices; |
|
|
|
/* "Welford's method" for online variance |
|
* see Knuth, TAoCP Volume 2, 3rd edn., p232 */ |
|
mainstats.nvariance_mbps += (slice_mbps - old_mean_mbps) * |
|
(slice_mbps - mainstats.mean_mbps); |
|
|
printf("Elapsed: %11llu Mbps: %11.3Lf Peak Mbps: %11.3Lf %s PPS: %7llu\n", |
printf("Elapsed: %11llu Mbps: %11.3Lf Peak Mbps: %11.3Lf %s PPS: %7llu\n", |
total_elapsed, slice_mbps, mainstats.peak_mbps, |
total_elapsed, slice_mbps, mainstats.peak_mbps, |
ptb->sflag ? "Rx" : "Tx", pps); |
ptb->sflag ? "Rx" : "Tx", pps); |
|
|
/* Clean up this slice time */ |
/* Clean up this slice time */ |
sc->t_last = t_cur; |
udp_sc->t_last = t_cur; |
sc->bytes = 0; |
udp_sc->bytes = 0; |
sc->udp_slice_pkts = 0; |
udp_sc->udp_slice_pkts = 0; |
|
|
|
mainstats.slice_bytes = 0; |
set_slice_timer(1); |
set_slice_timer(1); |
} |
} |
|
|
static void |
static void |
udp_server_handle_sc(int fd, short event, void *v_sc) |
udp_server_handle_sc(int fd, short event, void *bula) |
{ |
{ |
|
static int first_read = 1; |
ssize_t n; |
ssize_t n; |
struct statctx *sc = v_sc; |
|
|
|
n = read(fd, ptb->dummybuf, ptb->dummybuf_len); |
n = read(fd, ptb->dummybuf, ptb->dummybuf_len); |
if (n == 0) |
if (n == 0) |
|
|
|
|
if (ptb->vflag >= 3) |
if (ptb->vflag >= 3) |
fprintf(stderr, "read: %zd bytes\n", n); |
fprintf(stderr, "read: %zd bytes\n", n); |
/* If this was our first packet, start slice timer */ |
if (first_read) { |
if (mainstats.peak_mbps == 0) |
first_read = 0; |
|
stats_prepare(udp_sc); |
set_slice_timer(1); |
set_slice_timer(1); |
|
} |
/* Account packet */ |
/* Account packet */ |
sc->udp_slice_pkts++; |
udp_sc->udp_slice_pkts++; |
sc->bytes += n; |
udp_sc->bytes += n; |
|
mainstats.slice_bytes += n; |
|
mainstats.total_bytes += n; |
} |
} |
|
|
static void |
static void |
|
|
fprintf(stderr, "read: %zd bytes\n", n); |
fprintf(stderr, "read: %zd bytes\n", n); |
sc->bytes += n; |
sc->bytes += n; |
mainstats.slice_bytes += n; |
mainstats.slice_bytes += n; |
|
mainstats.total_bytes += n; |
} |
} |
|
|
static void |
static void |
|
|
sc->tcp_ts = ts; |
sc->tcp_ts = ts; |
sc->fd = sock; |
sc->fd = sock; |
stats_prepare(sc); |
stats_prepare(sc); |
|
|
event_set(&sc->ev, sc->fd, EV_READ | EV_PERSIST, |
event_set(&sc->ev, sc->fd, EV_READ | EV_PERSIST, |
tcp_server_handle_sc, sc); |
tcp_server_handle_sc, sc); |
event_add(&sc->ev, NULL); |
event_add(&sc->ev, NULL); |
TAILQ_INSERT_TAIL(&sc_queue, sc, entry); |
TAILQ_INSERT_TAIL(&sc_queue, sc, entry); |
|
|
mainstats.nconns++; |
mainstats.nconns++; |
if (mainstats.nconns == 1) |
if (mainstats.nconns == 1) |
set_slice_timer(1); |
set_slice_timer(1); |
|
|
} |
} |
|
|
static void |
static void |
server_init(struct addrinfo *aitop, struct statctx *udp_sc) |
server_init(struct addrinfo *aitop) |
{ |
{ |
char tmp[128]; |
char tmp[128]; |
int sock, on = 1; |
int sock, on = 1; |
|
|
if ((ev = calloc(1, sizeof(*ev))) == NULL) |
if ((ev = calloc(1, sizeof(*ev))) == NULL) |
err(1, "calloc"); |
err(1, "calloc"); |
event_set(ev, sock, EV_READ | EV_PERSIST, |
event_set(ev, sock, EV_READ | EV_PERSIST, |
udp_server_handle_sc, udp_sc); |
udp_server_handle_sc, NULL); |
event_add(ev, NULL); |
event_add(ev, NULL); |
} else { |
} else { |
if ((ts = calloc(1, sizeof(*ts))) == NULL) |
if ((ts = calloc(1, sizeof(*ts))) == NULL) |
|
|
if (errno == EINTR || errno == EWOULDBLOCK || |
if (errno == EINTR || errno == EWOULDBLOCK || |
(UDP_MODE && errno == ENOBUFS)) |
(UDP_MODE && errno == ENOBUFS)) |
return; |
return; |
err(1, "write"); |
warn("write"); |
|
wrapup(1); |
} |
} |
if (TCP_MODE && n == 0) { |
if (TCP_MODE && n == 0) { |
fprintf(stderr, "Remote end closed connection"); |
fprintf(stderr, "Remote end closed connection"); |
exit(1); |
wrapup(1); |
} |
} |
if (ptb->vflag >= 3) |
if (ptb->vflag >= 3) |
fprintf(stderr, "write: %zd bytes\n", n); |
fprintf(stderr, "write: %zd bytes\n", n); |
sc->bytes += n; |
sc->bytes += n; |
mainstats.slice_bytes += n; |
mainstats.slice_bytes += n; |
|
mainstats.total_bytes += n; |
if (UDP_MODE) |
if (UDP_MODE) |
sc->udp_slice_pkts++; |
sc->udp_slice_pkts++; |
} |
} |
|
|
static void |
static void |
client_init(struct addrinfo *aitop, int nconn, struct statctx *udp_sc, |
client_init(struct addrinfo *aitop, int nconn, struct addrinfo *aib) |
struct addrinfo *aib) |
|
{ |
{ |
struct statctx *sc; |
struct statctx *sc; |
struct addrinfo *ai; |
struct addrinfo *ai; |
char tmp[128]; |
char tmp[128]; |
int i, r, sock; |
int i, r, sock; |
|
|
sc = udp_sc; |
|
for (i = 0; i < nconn; i++) { |
for (i = 0; i < nconn; i++) { |
for (sock = -1, ai = aitop; ai != NULL; ai = ai->ai_next) { |
for (sock = -1, ai = aitop; ai != NULL; ai = ai->ai_next) { |
saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, |
saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, |
|
|
if (TCP_MODE) { |
if (TCP_MODE) { |
if ((sc = calloc(1, sizeof(*sc))) == NULL) |
if ((sc = calloc(1, sizeof(*sc))) == NULL) |
err(1, "calloc"); |
err(1, "calloc"); |
} |
} else |
|
sc = udp_sc; |
|
|
sc->fd = sock; |
sc->fd = sock; |
stats_prepare(sc); |
stats_prepare(sc); |
|
|
event_set(&sc->ev, sc->fd, EV_WRITE | EV_PERSIST, |
event_set(&sc->ev, sc->fd, EV_WRITE | EV_PERSIST, |
client_handle_sc, sc); |
client_handle_sc, sc); |
event_add(&sc->ev, NULL); |
event_add(&sc->ev, NULL); |
TAILQ_INSERT_TAIL(&sc_queue, sc, entry); |
TAILQ_INSERT_TAIL(&sc_queue, sc, entry); |
|
|
mainstats.nconns++; |
mainstats.nconns++; |
if (mainstats.nconns == 1) |
if (mainstats.nconns == 1) |
set_slice_timer(1); |
set_slice_timer(1); |
|
|
static void |
static void |
quit(int sig, short event, void *arg) |
quit(int sig, short event, void *arg) |
{ |
{ |
exit(0); |
wrapup(0); |
} |
} |
|
|
|
static void |
|
wrapup(int err) |
|
{ |
|
const int transfers = timerisset(&mainstats.t_first); |
|
const int stats = (mainstats.floor_mbps != INFINITY); |
|
|
|
if (transfers) { |
|
if (!stats) { |
|
if (UDP_MODE) |
|
udp_process_slice(0, 0, NULL); |
|
else |
|
tcp_process_slice(0, 0, NULL); |
|
} |
|
|
|
summary_display(); |
|
} |
|
|
|
exit(err); |
|
} |
|
|
int |
int |
main(int argc, char **argv) |
main(int argc, char **argv) |
{ |
{ |
|
|
struct nlist nl[] = { { "_tcbtable" }, { "" } }; |
struct nlist nl[] = { { "_tcbtable" }, { "" } }; |
const char *host = NULL, *port = DEFAULT_PORT, *srcbind = NULL; |
const char *host = NULL, *port = DEFAULT_PORT, *srcbind = NULL; |
struct event ev_sigint, ev_sigterm, ev_sighup, ev_progtimer; |
struct event ev_sigint, ev_sigterm, ev_sighup, ev_progtimer; |
struct statctx *udp_sc = NULL; |
|
struct sockaddr_un sock_un; |
struct sockaddr_un sock_un; |
|
|
/* Init world */ |
/* Init world */ |
|
|
drop_gid(); |
drop_gid(); |
|
|
if (!ptb->sflag || ptb->Uflag) |
if (!ptb->sflag || ptb->Uflag) |
host = argv[0]; |
mainstats.host = host = argv[0]; |
|
|
if (ptb->Uflag) |
if (ptb->Uflag) |
if (unveil(host, "rwc") == -1) |
if (unveil(host, "rwc") == -1) |
|
|
err(1, "malloc"); |
err(1, "malloc"); |
arc4random_buf(ptb->dummybuf, ptb->dummybuf_len); |
arc4random_buf(ptb->dummybuf, ptb->dummybuf_len); |
|
|
|
timerclear(&mainstats.t_first); |
|
mainstats.floor_mbps = INFINITY; |
|
|
/* Setup libevent and signals */ |
/* Setup libevent and signals */ |
event_init(); |
event_init(); |
signal_set(&ev_sigterm, SIGTERM, signal_handler, NULL); |
signal_set(&ev_sigterm, SIGTERM, signal_handler, NULL); |
|
|
if ((udp_sc = calloc(1, sizeof(*udp_sc))) == NULL) |
if ((udp_sc = calloc(1, sizeof(*udp_sc))) == NULL) |
err(1, "calloc"); |
err(1, "calloc"); |
udp_sc->fd = -1; |
udp_sc->fd = -1; |
stats_prepare(udp_sc); |
evtimer_set(&mainstats.timer, udp_process_slice, NULL); |
evtimer_set(&mainstats.timer, udp_process_slice, udp_sc); |
|
} else { |
} else { |
print_tcp_header(); |
print_tcp_header(); |
evtimer_set(&mainstats.timer, tcp_process_slice, NULL); |
evtimer_set(&mainstats.timer, tcp_process_slice, NULL); |
} |
} |
|
|
if (ptb->sflag) |
if (ptb->sflag) |
server_init(aitop, udp_sc); |
server_init(aitop); |
else { |
else { |
if (secs > 0) { |
if (secs > 0) { |
timerclear(&tv); |
timerclear(&tv); |
|
|
evtimer_set(&ev_progtimer, quit, NULL); |
evtimer_set(&ev_progtimer, quit, NULL); |
evtimer_add(&ev_progtimer, &tv); |
evtimer_add(&ev_progtimer, &tv); |
} |
} |
client_init(aitop, nconn, udp_sc, aib); |
client_init(aitop, nconn, aib); |
|
|
if (pledge("stdio", NULL) == -1) |
if (pledge("stdio", NULL) == -1) |
err(1, "pledge"); |
err(1, "pledge"); |