On Wed, Aug 05, 2009 at 07:04:21PM +0200, Claudio Jeker wrote:
> > 
> > Index: tcpbench.c
> > ===================================================================
> > RCS file: /cvs/src/usr.bin/tcpbench/tcpbench.c,v
> > retrieving revision 1.8
> > diff -d -p -u -r1.8 tcpbench.c
> > --- tcpbench.c      18 Sep 2008 10:23:33 -0000      1.8
> > +++ tcpbench.c      1 Aug 2009 22:32:09 -0000
> > @@ -18,6 +18,7 @@
> >  #include <sys/time.h>
> >  #include <sys/socket.h>
> >  #include <sys/socketvar.h>
> > +#include <sys/resource.h>
> >  
> >  #include <net/route.h>
> >  
> > @@ -47,22 +48,47 @@
> >  #include <kvm.h>
> >  #include <nlist.h>
> >  
> > -#define DEFAULT_PORT               "12345"
> > -#define DEFAULT_STATS_INTERVAL     1000            /* ms */
> > -#define DEFAULT_BUF                256 * 1024
> > +#define DEFAULT_PORT                   "12345"
> > +#define DEFAULT_STATS_INTERVAL         1000        /* ms */
> > +#define DEFAULT_BUF                    256 * 1024
> > +#define DEFAULT_MAINSTATS_COUNT     4           /* print mainstats
> > +                                              after 10 interactions */
> > +#define MAX_FD                      1024
> >  
> >  sig_atomic_t done = 0;
> > -sig_atomic_t print_stats = 0;
> > +sig_atomic_t proc_slice = 0;
> > +
> > +static char **kflag;
> > +static size_t Bflag;
> > +static int    Sflag;
> > +static int    mflag;
> > +static int    rflag;
> > +static int    sflag;
> > +static int    vflag;
> >  
> > +/* stats for a single connection */
> >  struct statctx {
> > -   struct timeval t_start, t_last, t_cur;
> > +   struct timeval t_start, t_last;
> >     unsigned long long bytes;
> > -   pid_t pid;
> >     u_long tcbaddr;
> > -   kvm_t *kh;
> >     char **kvars;
> > +   kvm_t *kh;
> >  };
> >  
> > +/*
> > + * We account the mainstats here, that is the stats
> > + * for all connections, all variables starting with slice
> > + * are used to account information for the timeslice
> > + * between each output. Peak variables record the highest
> > + * between all slices so far.
> > + */
> > +static struct {
> > +   unsigned long long slice_bytes; /* bytes for last slice */
> > +   struct timeval t_start;         /* when we started counting */
> > +   long double peak_mbps;          /* peak mbps so far */
> > +   int nconns;                     /* connected clients */
> > +} mainstats;
> > +
> >  /* When adding variables, also add to stats_display() */
> >  static const char *allowed_kvars[] = {
> >     "inpcb.inp_flags",
> > @@ -103,7 +129,7 @@ exitsighand(int signo)
> >  static void
> >  alarmhandler(int signo)
> >  {
> > -   print_stats = 1;
> > +   proc_slice = 1;
> >     signal(signo, alarmhandler);
> >  }
> >  
> > @@ -113,7 +139,7 @@ usage(void)
> >     fprintf(stderr,
> >         "usage: tcpbench -l\n"
> >         "       tcpbench [-v] [-B buf] [-k kvars] [-n connections]"
> > -       " [-p port] [-r rate]\n"
> > +       " [-p port] [-r rate] [-m interval]\n"
> >         "                [-S space] hostname\n"
> >         "       tcpbench -s [-v] [-B buf] [-k kvars] [-p port] [-r rate]"
> >         " [-S space]\n");
> > @@ -137,6 +163,40 @@ saddr_ntop(const struct sockaddr *addr, 
> >  }
> >  
> >  static void
> > +set_timer(int toggle)
> > +{
> > +   struct itimerval itv;
> > +
> > +   if (rflag <= 0)
> > +           return;
> > +
> > +   if (toggle) {
> > +           itv.it_interval.tv_sec = rflag / 1000;
> > +           itv.it_interval.tv_usec = (rflag % 1000) * 1000;
> > +           itv.it_value = itv.it_interval;
> > +   }
> > +   else
> > +           bzero(&itv, sizeof(itv));
> > +           
> > +   setitimer(ITIMER_REAL, &itv, NULL);
> > +}
> > +
> > +static void
> > +print_header(void)
> > +{
> > +   char **kv;
> > +   
> > +   printf("%12s %14s %12s %8s ", "elapsed_ms", "bytes", "mbps",
> > +       "bwidth");
> > +   
> > +   for (kv = kflag;  kflag != NULL && *kv != NULL; kv++) 
> > +           printf("%s%s", kv != kflag ? "," : "", *kv);
> > +   
> > +   printf("\n");
> > +   fflush(stdout);
> > +}
> > +
> > +static void
> >  kget(kvm_t *kh, u_long addr, void *buf, int size)
> >  {
> >     if (kvm_read(kh, addr, buf, size) != size)
> > @@ -144,7 +204,7 @@ kget(kvm_t *kh, u_long addr, void *buf, 
> >  }
> >  
> >  static u_long
> > -kfind_tcb(kvm_t *kh, u_long ktcbtab, int sock, int vflag)
> > +kfind_tcb(kvm_t *kh, u_long ktcbtab, int sock)
> >  {
> >     struct inpcbtable tcbtab;
> >     struct inpcb *head, *next, *prev;
> > @@ -304,98 +364,81 @@ check_prepare_kvars(char *list)
> >  }
> >  
> >  static void
> > -stats_prepare(struct statctx *sc, int fd, kvm_t *kh, u_long ktcbtab,
> > -    int rflag, int vflag, char **kflag)
> > +stats_prepare(struct statctx *sc, int fd, kvm_t *kh, u_long ktcbtab)
> >  {
> > -   struct itimerval itv;
> > -   int i;
> > -
> >     if (rflag <= 0)
> >             return;
> >     sc->kh = kh;
> >     sc->kvars = kflag;
> >     if (kflag)
> > -           sc->tcbaddr = kfind_tcb(kh, ktcbtab, fd, vflag);
> > -   gettimeofday(&sc->t_start, NULL);
> > +           sc->tcbaddr = kfind_tcb(kh, ktcbtab, fd);
> > +   if (gettimeofday(&sc->t_start, NULL) == -1)
> > +           err(1, "gettimeofday");
> >     sc->t_last = sc->t_start;
> > -   signal(SIGALRM, alarmhandler);
> > -   itv.it_interval.tv_sec = rflag / 1000;
> > -   itv.it_interval.tv_usec = (rflag % 1000) * 1000;
> > -   itv.it_value = itv.it_interval;
> > -   setitimer(ITIMER_REAL, &itv, NULL);
> >     sc->bytes = 0;
> > -   sc->pid = getpid();
> > -
> > -   printf("%8s %12s %14s %12s ", "pid", "elapsed_ms", "bytes", "Mbps");
> > -   if (sc->kvars != NULL) {
> > -           for (i = 0; sc->kvars[i] != NULL; i++)
> > -                   printf("%s%s", i > 0 ? "," : "", sc->kvars[i]);
> > -   }
> > -   printf("\n");
> > -   fflush(stdout);
> >  }
> >  
> >  static void
> >  stats_update(struct statctx *sc, ssize_t n)
> >  {
> >     sc->bytes += n;
> > +   mainstats.slice_bytes += n;
> >  }
> >  
> >  static void
> > -stats_display(struct statctx *sc)
> > +stats_cleanslice(void)
> >  {
> > -   struct timeval t_diff;
> > -   unsigned long long total_elapsed, since_last;
> > -   size_t i;
> > -   struct inpcb inpcb;
> > -   struct tcpcb tcpcb;
> > -   struct socket sockb;
> > -
> > -   gettimeofday(&sc->t_cur, NULL);
> > -   timersub(&sc->t_cur, &sc->t_start, &t_diff);
> > -   total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
> > -   timersub(&sc->t_cur, &sc->t_last, &t_diff);
> > -   since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
> > -   printf("%8ld %12llu %14llu %12.3Lf ", (long)sc->pid,
> > -       total_elapsed, sc->bytes,
> > -       (long double)(sc->bytes * 8) / (since_last * 1000.0));
> > -   sc->t_last = sc->t_cur;
> > -   sc->bytes = 0;
> > +   mainstats.slice_bytes = 0;
> > +}
> >  
> > +static void
> > +stats_display(unsigned long long total_elapsed, long double mbps,
> > +    float bwperc, struct statctx *sc, struct inpcb *inpcb,
> > +    struct tcpcb *tcpcb, struct socket *sockb)
> > +{
> > +   int j;
> > +   
> > +   printf("%12llu %14llu %12.3Lf %7.2f%% ", total_elapsed, sc->bytes,
> > +       mbps, bwperc);
> > +   
> >     if (sc->kvars != NULL) {
> > -           kupdate_stats(sc->kh, sc->tcbaddr, &inpcb, &tcpcb, &sockb);
> > -           for (i = 0; sc->kvars[i] != NULL; i++) {
> > -#define P(v, f) \
> > -   if (strcmp(sc->kvars[i], #v) == 0) { \
> > -           printf("%s"f, i > 0 ? "," : "", v); \
> > -           continue; \
> > -   }
> > -                   P(inpcb.inp_flags, "0x%08x")
> > -                   P(sockb.so_rcv.sb_cc, "%lu")
> > -                   P(sockb.so_rcv.sb_hiwat, "%lu")
> > -                   P(sockb.so_snd.sb_cc, "%lu")
> > -                   P(sockb.so_snd.sb_hiwat, "%lu")
> > -                   P(tcpcb.snd_una, "%u")
> > -                   P(tcpcb.snd_nxt, "%u")
> > -                   P(tcpcb.snd_wl1, "%u")
> > -                   P(tcpcb.snd_wl2, "%u")
> > -                   P(tcpcb.snd_wnd, "%lu")
> > -                   P(tcpcb.rcv_wnd, "%lu")
> > -                   P(tcpcb.rcv_nxt, "%u")
> > -                   P(tcpcb.rcv_adv, "%u")
> > -                   P(tcpcb.snd_max, "%u")
> > -                   P(tcpcb.snd_cwnd, "%lu")
> > -                   P(tcpcb.snd_ssthresh, "%lu")
> > -                   P(tcpcb.t_rcvtime, "%u")
> > -                   P(tcpcb.t_rtttime, "%u")
> > -                   P(tcpcb.t_rtseq, "%u")
> > -                   P(tcpcb.t_srtt, "%hu")
> > -                   P(tcpcb.t_rttvar, "%hu")
> > -                   P(tcpcb.t_rttmin, "%hu")
> > -                   P(tcpcb.max_sndwnd, "%lu")
> > -                   P(tcpcb.snd_scale, "%u")
> > -                   P(tcpcb.rcv_scale, "%u")
> > -                   P(tcpcb.last_ack_sent, "%u")
> > +           kupdate_stats(sc->kh, sc->tcbaddr, inpcb, tcpcb,
> > +               sockb);
> > +
> > +           for (j = 0; sc->kvars[j] != NULL; j++) {
> > +#define S(a) #a
> 
> This macro is used just a single time and I don't realy see the advantage
> of your new P() macro to the old one.

I had to do it in order to keep the same kvars interface at the
command line, prior sockb/inpcb/tcpcb were not a pointer, so the
contruct had to expand to sockb.inp_flags, and now to
sockb->inp_flags.

> 
> > +#define P(b, v, f)                                                 \
> > +                   if (strcmp(sc->kvars[j], S(b.v)) == 0) {        \
> > +                           printf("%s"f, j > 0 ? "," : "", b->v);  \
> > +                           continue;                               \
> > +                   }
> > +                   P(inpcb, inp_flags, "0x%08x")
> > +                   P(sockb, so_rcv.sb_cc, "%lu")
> > +                   P(sockb, so_rcv.sb_hiwat, "%lu")
> > +                   P(sockb, so_snd.sb_cc, "%lu")
> > +                   P(sockb, so_snd.sb_hiwat, "%lu")
> > +                   P(tcpcb, snd_una, "%u")
> > +                   P(tcpcb, snd_nxt, "%u")
> > +                   P(tcpcb, snd_wl1, "%u")
> > +                   P(tcpcb, snd_wl2, "%u")
> > +                   P(tcpcb, snd_wnd, "%lu")
> > +                   P(tcpcb, rcv_wnd, "%lu")
> > +                   P(tcpcb, rcv_nxt, "%u")
> > +                   P(tcpcb, rcv_adv, "%u")
> > +                   P(tcpcb, snd_max, "%u")
> > +                   P(tcpcb, snd_cwnd, "%lu")
> > +                   P(tcpcb, snd_ssthresh, "%lu")
> > +                   P(tcpcb, t_rcvtime, "%u")
> > +                   P(tcpcb, t_rtttime, "%u")
> > +                   P(tcpcb, t_rtseq, "%u")
> > +                   P(tcpcb, t_srtt, "%hu")
> > +                   P(tcpcb, t_rttvar, "%hu")
> > +                   P(tcpcb, t_rttmin, "%hu")
> > +                   P(tcpcb, max_sndwnd, "%lu")
> > +                   P(tcpcb, snd_scale, "%u")
> > +                   P(tcpcb, rcv_scale, "%u")
> > +                   P(tcpcb, last_ack_sent, "%u")
> > +#undef S                       
> >  #undef P
> >             }
> >     }
> > @@ -404,94 +447,104 @@ stats_display(struct statctx *sc)
> >  }
> >  
> >  static void
> > -stats_finish(struct statctx *sc)
> > +mainstats_display(long double slice_mbps, long double avg_mbps)
> >  {
> > -   struct itimerval itv;
> > +   printf("Conn: %3d Mbps: %12.3Lf Peak Mbps: %12.3Lf Avg Mbps: %12.3Lf\n",
> > +       mainstats.nconns, slice_mbps, mainstats.peak_mbps, avg_mbps); 
> >  
> > -   signal(SIGALRM, SIG_DFL);
> > -   bzero(&itv, sizeof(itv));
> > -   setitimer(ITIMER_REAL, &itv, NULL);
> > +   fflush(stdout);
> 
> Is the fflush(0 realy needed. printf() will flush on \n
> automatically.

True, not needed, removing.

> 
> >  }
> >  
> > -static void __dead
> > -handle_connection(kvm_t *kvmh, u_long ktcbtab, int sock, int vflag,
> > -    int rflag, char **kflag, int Bflag)
> > +static void
> > +process_slice(struct statctx *sc, size_t nsc)
> >  {
> > -   char *buf;
> > -   struct pollfd pfd;
> > -   ssize_t n;
> > -   int r;
> > -   struct statctx sc;
> > -
> > -   if ((buf = malloc(Bflag)) == NULL)
> > -           err(1, "malloc");
> > -   if ((r = fcntl(sock, F_GETFL, 0)) == -1)
> > -           err(1, "fcntl(F_GETFL)");
> > -   r |= O_NONBLOCK;
> > -   if (fcntl(sock, F_SETFL, r) == -1)
> > -           err(1, "fcntl(F_SETFL, O_NONBLOCK)");
> > +   int i;
> > +   static int total_display = 0;
> > +   float bwperc;
> > +   long double mbps, slice_mbps = 0;
> > +   unsigned long long total_elapsed, since_last;
> > +   struct timeval t_cur, t_diff;
> > +   struct inpcb inpcb;
> > +   struct tcpcb tcpcb;
> > +   struct socket sockb;
> >  
> > -   signal(SIGINT, exitsighand);
> > -   signal(SIGTERM, exitsighand);
> > -   signal(SIGHUP, exitsighand);
> > -   signal(SIGPIPE, SIG_IGN);
> > +   if (gettimeofday(&t_cur, NULL) == -1)
> > +           err(1, "gettimeofday");
> >  
> > -   bzero(&pfd, sizeof(pfd));
> > -   pfd.fd = sock;
> > -   pfd.events = POLLIN;
> > +   for (i = 0; i < nsc; i++, sc++) {
> > +           if (sc->kvars != NULL) /* process kernel stats */
> > +                   kupdate_stats(sc->kh, sc->tcbaddr, &inpcb, &tcpcb,
> > +                       &sockb);
> > +           timersub(&t_cur, &sc->t_start, &t_diff);
> > +           total_elapsed = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
> > +           timersub(&t_cur, &sc->t_last, &t_diff);
> > +           since_last = t_diff.tv_sec * 1000 + t_diff.tv_usec / 1000;
> > +           bwperc = (sc->bytes * 100.0) / mainstats.slice_bytes;
> > +           mbps = (sc->bytes * 8) / (since_last * 1000.0);
> > +           slice_mbps += mbps;
> > +           
> > +           stats_display(total_elapsed, mbps, bwperc, sc,
> > +               &inpcb, &tcpcb, &sockb);
> > +           
> > +           sc->t_last = t_cur;
> > +           sc->bytes = 0;
> >  
> > -   stats_prepare(&sc, sock, kvmh, ktcbtab, rflag, vflag, kflag);
> > +   }
> >  
> > -   while (!done) {
> > -           if (print_stats) {
> > -                   stats_display(&sc);
> > -                   print_stats = 0;
> > -           }
> > -           if (poll(&pfd, 1, INFTIM) == -1) {
> > -                   if (errno == EINTR)
> > -                           continue;
> > -                   err(1, "poll");
> > -           }
> > -           if ((n = read(pfd.fd, buf, Bflag)) == -1) {
> > -                   if (errno == EINTR || errno == EAGAIN)
> > -                           continue;
> > -                   err(1, "read");
> > -           }
> > -           if (n == 0) {
> > -                   fprintf(stderr, "%8ld closed by remote end\n",
> > -                       (long)getpid());
> > -                   done = -1;
> > -                   break;
> > -           }
> > -           if (vflag >= 3)
> > -                   fprintf(stderr, "read: %zd bytes\n", n);
> > -           stats_update(&sc, n);
> > +   /* process stats for this slice */
> > +   if (slice_mbps > mainstats.peak_mbps)
> > +           mainstats.peak_mbps = slice_mbps;
> > +   if (++total_display == mflag) {
> > +           mainstats_display(slice_mbps, slice_mbps / mainstats.nconns);
> > +           total_display = 0;
> >     }
> > -   stats_finish(&sc);
> > +}
> >  
> > -   free(buf);
> > -   close(sock);
> > -   exit(1);
> > +static int
> > +handle_connection(struct statctx *sc, int fd, char *buf, size_t buflen)
> > +{
> > +   ssize_t n;
> > +
> > +again:
> > +   n = read(fd, buf, buflen);
> > +   if (n == -1) {
> > +           fprintf(stderr, "fd = %d read errno = %d\n",fd, errno);
> 
> use warn() or strerror() to make the errno human understandable.
> Shouldn't the fprintf be moved after the EINTR and EWOULDBLOCK
> check?

Correct, this was a mistake, I was measuring EWOULDBLOCK hits and
forgot to take out the fprintf, I'll correct it and put it after EINTR
and EWOULDBLOCK checks.

> 
> > +           if (errno == EINTR)
> > +                   goto again;
> > +           else if (errno == EWOULDBLOCK) 
> > +                   return 0;
> > +           return -1;
> > +   }
> > +   else if (n == 0) {
> > +           fprintf(stderr, "%8d closed by remote end\n", fd);
> > +           close(fd);
> > +           return -1;
> > +   }
> > +   if (vflag >= 3)
> > +           fprintf(stderr, "read: %zd bytes\n", n);
> > +   
> > +   stats_update(sc, n);
> > +   return 0;
> >  }
> >  
> > -static void __dead
> > -serverloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop,
> > -    int vflag, int rflag, char **kflag, int Sflag, int Bflag)
> > +static int
> > +serverbind(struct pollfd *pfd, nfds_t max_nfds, struct addrinfo *aitop)
> >  {
> >     char tmp[128];
> > -   int r, sock, client_id, on = 1;
> > +   int sock, on = 1;
> >     struct addrinfo *ai;
> > -   struct pollfd *pfd;
> > -   struct sockaddr_storage ss;
> > -   socklen_t sslen;
> > -   size_t nfds, i, j;
> > +   int lnfds;
> >  
> > -   pfd = NULL;
> > -   nfds = 0;
> > +   lnfds = 0;
> >     for (ai = aitop; ai != NULL; ai = ai->ai_next) {
> > +           if (lnfds == max_nfds) {
> > +                   fprintf(stderr,
> > +                       "maximum number of listening fds reached\n");
> > +                   break;
> > +           }
> >             saddr_ntop(ai->ai_addr, ai->ai_addrlen, tmp, sizeof(tmp));
> >             if (vflag)
> > -                   fprintf(stderr, "Try listen on %s\n", tmp);
> > +                   fprintf(stderr, "Try to listen on %s\n", tmp);
> >             if ((sock = socket(ai->ai_family, ai->ai_socktype,
> >                 ai->ai_protocol)) == -1) {
> >                     if (ai->ai_next == NULL)
> > @@ -519,109 +572,189 @@ serverloop(kvm_t *kvmh, u_long ktcbtab, 
> >             if (listen(sock, 64) == -1) {
> >                     if (ai->ai_next == NULL)
> >                             err(1, "listen");
> > -                   if (vflag)
> > -                           warn("listen");
> 
> Why removing this? It would be nice to include the address/port that
> failed though.

Oooops, correct :)

> 
> >                     close(sock);
> >                     continue;
> >             }
> > -           if (nfds > 128)
> > -                   break;
> > -           if ((pfd = realloc(pfd, ++nfds * sizeof(*pfd))) == NULL)
> > -                   errx(1, "realloc(pfd * %zu)", nfds);
> > -           pfd[nfds - 1].fd = sock;
> > -           pfd[nfds - 1].events = POLLIN;
> > +           if (vflag >= 3)
> > +                   fprintf(stderr, "listening on fd %d\n", sock);
> > +           lnfds++;
> > +           pfd[lnfds - 1].fd = sock;
> > +           pfd[lnfds - 1].events = POLLIN;
> > +
> >     }
> >     freeaddrinfo(aitop);
> > -   if (nfds == 0)
> > +   if (lnfds == 0)
> >             errx(1, "No working listen addresses found");
> >  
> > -   signal(SIGINT, exitsighand);
> > -   signal(SIGTERM, exitsighand);
> > -   signal(SIGHUP, exitsighand);
> > -   signal(SIGPIPE, SIG_IGN);
> > -   signal(SIGCHLD, SIG_IGN);
> > +   return lnfds;
> > +}  
> > +
> > +static void
> > +set_listening(struct pollfd *pfd, nfds_t lfds, int toggle) {
> > +   int i;
> > +
> > +   for (i = 0; i < lfds; i++) {
> > +           fprintf(stderr, "tirando fd %d\n",
> 
> Wrong language.

Another oops.

> 
> > +               pfd[i].fd);
> > +           if (toggle)
> > +                   pfd[i].events = POLLIN;
> > +           else
> > +                   pfd[i].events = 0;
> > +   }
> > +                   
> > +}
> > +static void __dead
> > +serverloop(kvm_t *kvmh, u_long ktcbtab, struct addrinfo *aitop)
> > +{
> > +   struct rlimit rl;
> > +   socklen_t sslen;
> > +   struct pollfd *pfd;
> > +   char tmp[128], *buf;
> > +   struct statctx *psc;
> > +   struct sockaddr_storage ss;
> > +   nfds_t nfds, lfds;
> > +   size_t nalloc;
> > +   int i, r, sock, client_id;
> >  
> > +   sslen = sizeof(ss);
> > +   if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
> > +           err(1, "getrlimit");
> > +   if (rl.rlim_cur < MAX_FD)
> > +           rl.rlim_cur = MAX_FD;
> > +   if (setrlimit(RLIMIT_NOFILE, &rl))
> > +           err(1, "setrlimit");
> > +   if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
> > +           err(1, "getrlimit");
> > +   
> > +   nalloc = 128;
> > +   if ((pfd = calloc(sizeof(*pfd), nalloc)) == NULL)
> > +           err(1, "calloc");
> > +   if ((psc = calloc(sizeof(*psc), nalloc)) == NULL)
> > +           err(1, "calloc");
> > +   if ((buf = malloc(Bflag)) == NULL)
> > +           err(1, "malloc");
> > +   lfds = nfds = serverbind(pfd, nalloc - 1, aitop);
> > +   if (vflag >= 3)
> > +           fprintf(stderr, "listening on %d fds\n", lfds);
> >     if (setpgid(0, 0) == -1)
> >             err(1, "setpgid");
> > -
> > +   
> > +   print_header();
> > +   
> >     client_id = 0;
> > -   while (!done) {         
> > +   while (!done) {
> > +           if (proc_slice) { 
> > +                   process_slice(psc + lfds, nfds - lfds);
> > +                   stats_cleanslice();
> > +                   proc_slice = 0;
> > +           }
> > +           if (vflag >= 3) 
> > +                   fprintf(stderr, "mainstats.nconns = %u\n",
> > +                       mainstats.nconns);
> >             if ((r = poll(pfd, nfds, INFTIM)) == -1) {
> >                     if (errno == EINTR)
> >                             continue;
> >                     warn("poll");
> >                     break;
> >             }
> > +
> >             if (vflag >= 3)
> >                     fprintf(stderr, "poll: %d\n", r);
> >             for (i = 0 ; r > 0 && i < nfds; i++) {
> >                     if ((pfd[i].revents & POLLIN) == 0)
> >                             continue;
> > -                   if (vflag >= 3)
> > -                           fprintf(stderr, "fd %d active\n", pfd[i].fd);
> > +                   if (pfd[i].fd == -1)
> > +                           errx(1, "pfd insane");
> >                     r--;
> > -                   sslen = sizeof(ss);
> > -                   if ((sock = accept(pfd[i].fd, (struct sockaddr *)&ss,
> > -                       &sslen)) == -1) {
> > -                           if (errno == EINTR)
> > +                   if (vflag >= 3)
> > +                           fprintf(stderr, "fd %d active i = %d\n",
> > +                               pfd[i].fd, i);
> > +                   /* new connection */
> > +                   if (i < lfds) {
> > +                           if ((sock = accept(pfd[i].fd,
> > +                               (struct sockaddr *)&ss,
> > +                               &sslen)) == -1) {
> > +                                   if (errno == EINTR)
> > +                                           continue;
> > +                                   else if (errno == EMFILE ||
> > +                                       errno == ENFILE)
> > +                                           set_listening(pfd, lfds, 0);
> > +                                   warn("accept");
> >                                     continue;
> > -                           warn("accept");
> > -                           break;
> > +                           }
> > +                           if ((r = fcntl(sock, F_GETFL, 0)) == -1)
> > +                                   err(1, "fcntl(F_GETFL)");
> > +                           r |= O_NONBLOCK;
> > +                           if (fcntl(sock, F_SETFL, r) == -1)
> > +                                   err(1, "fcntl(F_SETFL, O_NONBLOCK)");
> > +                           saddr_ntop((struct sockaddr *)&ss, sslen,
> > +                               tmp, sizeof(tmp));
> > +                           if (vflag)
> > +                                   fprintf(stderr,
> > +                                       "Accepted connection %d from "
> > +                                       "%s, fd = %d\n", client_id++, tmp,
> > +                                        sock);
> > +                           /* alloc more space if we're full */
> > +                           if (nfds == nalloc) {
> > +                                   nalloc *= 2;
> > +                                   if ((pfd = realloc(pfd,
> > +                                       sizeof(*pfd) * nalloc)) == NULL)
> > +                                           err(1, "realloc");
> > +                                   if ((psc = realloc(psc,
> > +                                       sizeof(*psc) * nalloc)) == NULL)
> > +                                           err(1, "realloc");
> > +                           }
> > +                           stats_prepare(&psc[nfds], sock, kvmh, ktcbtab);
> > +                           pfd[nfds].fd = sock;
> > +                           pfd[nfds].events = POLLIN;
> > +                           nfds++;
> > +                           if (!mainstats.nconns++)
> > +                                   set_timer(1);
> > +                           continue;
> >                     }
> > -                   saddr_ntop((struct sockaddr *)&ss, sslen,
> > -                       tmp, sizeof(tmp));
> > -                   if (vflag)
> > -                           fprintf(stderr, "Accepted connection %d from "
> > -                               "%s, fd = %d\n", client_id++, tmp, sock);
> > -                   switch (fork()) {
> > -                   case -1:
> > -                           warn("fork");
> > -                           done = -1;
> > -                           break;
> > -                   case 0:
> > -                           for (j = 0; j < nfds; j++)
> > -                                   if (j != i)
> > -                                           close(pfd[j].fd);
> > -                           handle_connection(kvmh, ktcbtab, sock,
> > -                               vflag, rflag, kflag, Bflag);
> > -                           /* NOTREACHED */
> > -                           _exit(1);
> > -                   default:
> > -                           close(sock);
> > -                           break;
> > +                   /* event in fd */
> > +                   if (vflag >= 3)
> > +                           fprintf(stderr,
> > +                               "fd %d active", pfd[i].fd);
> > +                   if (handle_connection(&psc[i], pfd[i].fd,
> > +                       buf, Bflag) == -1) {
> > +                           pfd[i] = pfd[nfds - 1];
> > +                           pfd[nfds].fd = -1;
> 
> This feels wrong. You swap with a not yet checked fd so that one is
> skipped at the same time you don't reset the flags so if that fd had data
> you would end up with a -1 fd that you try to process.

I'm not sure if I understood you.

Doing like this we guarantee that poll is not looking at any structure
with a disabled fd, we always keep all interested fds lined up, so the
last one + 1 is always the next free.

I didn't pay attention to it in my first implementation and there was
a significant drop in performance, sometimes poll would be called with
n == 600+ but there were only a few interested fds.

But yes, this is not optimal because we may only process that swapped
fd in the next loop iteraction. Maybe we could avoid this by
decrementing i when we have a disconnection ? so we would force the
processing of the swapped fd.

> 
> > +                           psc[i] = psc[nfds - 1];
> > +                           mainstats.nconns--;
> > +                           nfds--;
> > +                           /* stop display if no clients */
> > +                           if (!mainstats.nconns) {
> > +                                   proc_slice = 1;
> > +                                   set_timer(0);
> > +                           }
> > +                           /* if we were full */
> > +                           set_listening(pfd, lfds, 1);
> >                     }
> > -                   if (done == -1)
> > -                           break;
> >             }
> >     }
> > -   for (i = 0; i < nfds; i++)
> > -           close(pfd[i].fd);
> > -   if (done > 0)
> > -           warnx("Terminated by signal %d", done);
> > -   signal(SIGTERM, SIG_IGN);
> > -   killpg(0, SIGTERM);
> >     exit(1);
> >  }
> >  
> >  static void __dead
> > -clientloop(kvm_t *kvmh, u_long ktcbtab, const char *host, const char *port,
> > -    int vflag, int rflag, char **kflag, int Sflag, int Bflag, int nconn)
> > +clientloop(kvm_t *kvmh, u_long ktcbtab, const char *host, const char 
> > *port, int nconn)
> >  {
> > -   char tmp[128];
> > -   char *buf;
> > -   int r, sock, herr;
> >     struct addrinfo *aitop, *ai, hints;
> > +   struct statctx *psc;
> >     struct pollfd *pfd;
> > -   ssize_t n;
> > -   struct statctx sc;
> > +   char tmp[128], *buf;
> > +   int r, herr, sock = -1;
> >     u_int i, scnt = 0;
> > +   ssize_t n;
> >  
> >     if ((buf = malloc(Bflag)) == NULL)
> >             err(1, "malloc");
> >  
> > -   if ((pfd = calloc(nconn, sizeof(struct pollfd))) == NULL)
> > +   if ((pfd = calloc(nconn, sizeof(*pfd))) == NULL)
> >             err(1, "clientloop pfd calloc");
> > -
> > +   if ((psc = calloc(nconn, sizeof(*psc))) == NULL)
> > +           err(1, "clientloop psc calloc");
> > +   
> >     for (i = 0; i < nconn; i++) {
> >             bzero(&hints, sizeof(hints));
> >             hints.ai_socktype = SOCK_STREAM;
> > @@ -674,6 +807,8 @@ clientloop(kvm_t *kvmh, u_long ktcbtab, 
> >  
> >             pfd[i].fd = sock;
> >             pfd[i].events = POLLOUT;
> > +           stats_prepare(psc + i, sock, kvmh, ktcbtab);
> > +           mainstats.nconns++;
> >             scnt++;
> >     }
> >  
> > @@ -681,17 +816,14 @@ clientloop(kvm_t *kvmh, u_long ktcbtab, 
> >             fprintf(stderr, "%u connections established\n", scnt);
> >     arc4random_buf(buf, Bflag);
> >  
> > -   signal(SIGINT, exitsighand);
> > -   signal(SIGTERM, exitsighand);
> > -   signal(SIGHUP, exitsighand);
> > -   signal(SIGPIPE, SIG_IGN);
> > -
> > -   stats_prepare(&sc, sock, kvmh, ktcbtab, rflag, vflag, kflag);
> > +   print_header();
> > +   set_timer(1);
> >  
> >     while (!done) {
> > -           if (print_stats) {
> > -                   stats_display(&sc);
> > -                   print_stats = 0;
> > +           if (proc_slice) {
> > +                   process_slice(psc, scnt);
> > +                   stats_cleanslice();
> > +                   proc_slice = 0;
> >             }
> >             if (poll(pfd, nconn, INFTIM) == -1) {
> >                     if (errno == EINTR)
> > @@ -713,12 +845,11 @@ clientloop(kvm_t *kvmh, u_long ktcbtab, 
> >                             if (vflag >= 3)
> >                                     fprintf(stderr, "write: %zd bytes\n",
> >                                         n);
> > -                           stats_update(&sc, n);
> > +                           stats_update(psc + i, n);
> >                     }
> >             }
> >     }
> > -   stats_finish(&sc);
> > -
> > +   
> >     if (done > 0)
> >             warnx("Terminated by signal %d", done);
> >  
> > @@ -744,20 +875,23 @@ main(int argc, char **argv)
> >     extern char *optarg;
> >  
> >     char kerr[_POSIX2_LINE_MAX], *tmp;
> > -   const char *errstr;
> > -   int ch, herr;
> >     struct addrinfo *aitop, hints;
> > +   const char *errstr;
> >     kvm_t *kvmh = NULL;
> > +   int ch, herr;
> >  
> >     const char *host = NULL, *port = DEFAULT_PORT;
> > -   char **kflag = NULL;
> > -   int sflag = 0, vflag = 0, rflag = DEFAULT_STATS_INTERVAL, Sflag = 0;
> > -   int Bflag = DEFAULT_BUF;
> >     int nconn = 1;
> >  
> > +   Bflag = DEFAULT_BUF;
> > +   Sflag = sflag = vflag = 0;
> > +   kflag = NULL;
> > +   rflag = DEFAULT_STATS_INTERVAL;
> > +   mflag = DEFAULT_MAINSTATS_COUNT;
> > +
> >     struct nlist nl[] = { { "_tcbtable" }, { "" } };
> >  
> > -   while ((ch = getopt(argc, argv, "B:hlk:n:p:r:sS:v")) != -1) {
> > +   while ((ch = getopt(argc, argv, "B:hlk:m:n:p:r:sS:v")) != -1) {
> >             switch (ch) {
> >             case 'l':
> >                     list_kvars();
> > @@ -775,6 +909,12 @@ main(int argc, char **argv)
> >                             errx(1, "statistics interval is %s: %s",
> >                                 errstr, optarg);
> >                     break;
> > +           case 'm':
> > +                   mflag = strtonum(optarg, 1, 65535, &errstr); 
> > +                   if (errstr != NULL)
> > +                           errx(1, "mainstats interval is %s: %s",
> > +                               errstr, optarg);
> > +                   break;
> >             case 'p':
> >                     port = optarg;
> >                     break;
> > @@ -815,9 +955,6 @@ main(int argc, char **argv)
> >     if (argc != (sflag ? 0 : 1))
> >             usage();
> >  
> > -   if (kflag != NULL && nconn > 1)
> > -           errx(1, "-k currently only works with a single tcp connection");
> > -
> >     if (!sflag)
> >             host = argv[0];
> >  
> > @@ -843,12 +980,16 @@ main(int argc, char **argv)
> >     } else
> >             drop_gid();
> >  
> > +   signal(SIGINT, exitsighand);
> > +   signal(SIGTERM, exitsighand);
> > +   signal(SIGHUP, exitsighand);
> > +   signal(SIGPIPE, SIG_IGN);
> > +   signal(SIGALRM, alarmhandler);
> > +
> >     if (sflag)
> > -           serverloop(kvmh, nl[0].n_value, aitop, vflag, rflag, kflag,
> > -               Sflag, Bflag);
> > +           serverloop(kvmh, nl[0].n_value, aitop);
> >     else
> > -           clientloop(kvmh, nl[0].n_value, host, port, vflag, rflag, kflag,
> > -               Sflag, Bflag, nconn);
> > +           clientloop(kvmh, nl[0].n_value, host, port, nconn);
> >  
> >     return 0;
> >  }
> > 
> 
> This needs a bit more work and maybe it would make sense to switch away
> from poll to kqueue or libevent. poll() gets inefficient when handling
> large ammount of fds. But that's maybe for later.

Cool, thanks a lot for your time, I'll do the corrections that you
pointed.

-- 
Christiano Farina HAESBAERT
Do NOT send me html mail.

Reply via email to