*BSD News Article 35219


Return to BSD News archive

Xref: sserve comp.unix.internals:7637 comp.unix.solaris:22170 comp.unix.sys5.r4:8264 comp.unix.bsd:14795
Path: sserve!newshost.anu.edu.au!harbinger.cc.monash.edu.au!bunyip.cc.uq.oz.au!munnari.oz.au!constellation!paladin.american.edu!europa.eng.gtefsd.com!library.ucla.edu!csulb.edu!nic-nac.CSU.net!usc!math.ohio-state.edu!cs.utexas.edu!chpc.utexas.edu!news.utdallas.edu!wupost!bigfoot.wustl.edu!tango.cs.wustl.edu!not-for-mail
From: schmidt@tango.cs.wustl.edu (Douglas C. Schmidt)
Newsgroups: comp.unix.internals,comp.unix.solaris,comp.unix.sys5.r4,comp.unix.bsd
Subject: Re: DLPI Vs BSD Sockets Vs Sys V TLI
Date: 25 Aug 1994 18:26:03 -0500
Organization: Computer Science Department, Washington University.
Lines: 1083
Distribution: inet
Message-ID: <33j9ab$bnq@tango.cs.wustl.edu>
References: <33ft3u$mn3@news.tamu.edu> <33iecq$leg@crl2.crl.com> <1994Aug25.165610.18709@noao.edu>
NNTP-Posting-Host: tango.cs.wustl.edu

In article <1994Aug25.165610.18709@noao.edu>,
W. Richard Stevens <rstevens@noao.edu> wrote:
++ Have you ever benchmarked sockets versus TLI on Solaris?  Has anyone?
++ How about just changing ttcp to use TLI instead of sockets and proving
++ or disproving this myth?

I've ported ttcp to use TLI on Solaris.  I just ran a test using
loop-back mode on my SPARCstation 20 running Solaris 2.3 to compare
the two.

	Here are the results from the socket version (no additional
options):

----------------------------------------
% ./sock-ttcp -s -r -f m -p 5000
ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5000  tcp
ttcp-r: socket
ttcp-r: accept from 127.0.0.1
ttcp-r: 16777216 bytes in 2.24 real seconds = 57.16 Mbit/sec +++
ttcp-r: 2049 I/O calls, msec/call = 1.12, calls/sec = 915.07
ttcp-r: 0.0user 0.7sys 0:02real 34%
----------------------------------------

	And here what I get with the TLI version (also no additional
options):

----------------------------------------
% ./tli-ttcp -s -r -f m -p 5002
ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5002  tcp
ttcp-r: t_open
ttcp-r: t_accept from 127.0.0.1
ttcp-r: 16777216 bytes in 2.37 real seconds = 54.00 Mbit/sec +++
ttcp-r: 2049 I/O calls, msec/call = 1.18, calls/sec = 864.44
ttcp-r: 0.1user 0.8sys 0:02real 42%
----------------------------------------

	Although the results aren't strikingly different it is
interestingly to note that the socket version of TTCP is faster than
the TLI version (I ran this several times and the socket version
consistently outperformed the TLI version).  Moreover, the percentage
utilization of the CPU is also higher for the TLI version.  I think
this confirms Rich's intuition that sockets are not inherently slower
than TLI.

	BTW, I've posted the TLI-based version of TTCP below.  Please
let me know if you have any comments.

	Thanks,
	
		Doug

----------------------------------------
/*
 *      T T C P . C
 *
 * Test TCP connection.  Makes a connection on port 5001
 * and transfers fabricated buffers or data copied from stdin.
 *
 * Usable on 4.2, 4.3, and 4.1a systems by defining one of
 * BSD42 BSD43 (BSD41a)
 * Machines using System V with BSD sockets should define SYSV.
 *
 * Modified for operation under 4.2BSD, 18 Dec 84
 *      T.C. Slattery, USNA
 * Minor improvements, Mike Muuss and Terry Slattery, 16-Oct-85.
 * Modified in 1989 at Silicon Graphics, Inc.
 *      catch SIGPIPE to be able to print stats when receiver has died
 *      for tcp, don't look for sentinel during reads to allow small transfers
 *      increased default buffer size to 8K, nbuf to 2K to transfer 16MB
 *      moved default port to 5001, beyond IPPORT_USERRESERVED
 *      make sinkmode default because it is more popular,
 *              -s now means don't sink/source
 *      count number of read/write system calls to see effects of
 *              blocking from full socket buffers
 *      for tcp, -D option turns off buffered writes (sets TCP_NODELAY sockopt)
 *      buffer alignment options, -A and -O
 *      print stats in a format that's a bit easier to use with grep & awk
 *      for SYSV, mimic BSD routines to use most of the existing timing code
 * Modified by Steve Miller of the University of Maryland, College Park
 *      -b sets the socket buffer size (SO_SNDBUF/SO_RCVBUF)
 * Modified Sept. 1989 at Silicon Graphics, Inc.
 *      restored -s sense at request of tcs@brl
 * Modified Oct. 1991 at Silicon Graphics, Inc.
 *      use getopt(3) for option processing, add -f and -T options.
 *      SGI IRIX 3.3 and 4.0 releases don't need #define SYSV.
 * Modified by Douglas C. Schmidt August 10. 1993 
        rewrote ttcp to run atop TLI rather than sockets
 * Distribution Status -
 *      Public Domain.  Distribution Unlimited.
 */
#ifndef lint
static char RCSid[] = "ttcp.c $Revision: 1.9 $";
#endif

/* #define BSD43 */
/* #define BSD42 */
/* #define BSD41a */
#define SYSV /* required on SGI IRIX releases before 3.3 */

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>		/* struct itimerval */
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <tiuser.h>

/* Set the socket options using TLI conventions... */

typedef void (*SIG_TYP)();

#if defined (__GNUC__)
struct opthdr {
	long	level;		/* protocol level affected */
	long	name;		/* option to modify */
	long	len;		/* length of option value */
};
extern int t_errno;
#endif

#define SET_SO_OPTION(SOCHDR, LEVEL, OPTION, SIZE, VALUE) \
  do { SOCHDR.opthdr.level = LEVEL; SOCHDR.opthdr.name = OPTION; \
       SOCHDR.opthdr.len = SIZE; SOCHDR.value = VALUE; } while (0)

#if defined(SYSV)
#include <sys/times.h>
#include <sys/param.h>
struct rusage
{
  struct timeval ru_utime, ru_stime;
};

#define RUSAGE_SELF 0
#else
#include <sys/resource.h>
#define memcpy(A,B,C) bcopy ((A),(B),(C))
#define memset(A,B,C) bzero((A),(C))
#endif /* SYSV */

struct sockaddr_in sinme;
struct sockaddr_in sinhim;
struct sockaddr_in frominet;

int fromlen;
int fd;                         /* fd of network socket */

int buflen = 8 * 1024;          /* length of buffer */
char *buf;                      /* ptr to dynamic buffer */
int nbuf = 2 * 1024;            /* number of buffers to send in sinkmode */

int bufoffset = 0;              /* align buffer to this */
int bufalign = 16 * 1024;       /* modulo this */

int udp = 0;                    /* 0 = tcp, !0 = udp */
int options = 0;                /* socket options */
int one = 1;                    /* for 4.3 BSD style setsockopt() */
short port = 5001;              /* TCP port number */
char *host;                     /* ptr to name of host */
int trans;                      /* 0=receive, !0=transmit mode */
int sinkmode = 0;               /* 0=normal I/O, !0=sink/source mode */
int verbose = 0;                /* 0=print basic info, 1=print cpu rate, proc
                                 * resource usage. */
int nodelay = 0;                /* set TCP_NODELAY socket option */
int b_flag = 0;                 /* use mread() */
int sockbufsize = 0;            /* socket buffer size to use */
char fmt = 'K';                 /* output format: k = kilobits, K = kilobytes,
                                 *  m = megabits, M = megabytes,
                                 *  g = gigabits, G = gigabytes */
int touchdata = 0;              /* access data after reading */

struct hostent *addr;
extern int errno;
extern int optind;
extern char *optarg;

char Usage[] = "\
Usage: ttcp -t [-options] host [ < in ]\n\
       ttcp -r [-options > out]\n\
Common options:\n\
        -l ##   length of bufs read from or written to network (default 8192)\n\
        -u      use UDP instead of TCP\n\
        -p ##   port number to send to or listen at (default 5001)\n\
        -s      -t: source a pattern to network\n\
                -r: sink (discard) all data from network\n\
        -A      align the start of buffers to this modulus (default 16384)\n\
        -O      start buffers at this offset from the modulus (default 0)\n\
        -v      verbose: print more statistics\n\
        -d      set SO_DEBUG socket option\n\
        -b ##   set socket buffer size (if supported)\n\
        -f X    format for rate: k,K = kilo{bit,byte}; m,M = mega; g,G = giga\n\
Options specific to -t:\n\
        -n##    number of source bufs written to network (default 2048)\n\
        -D      don't buffer TCP writes (sets TCP_NODELAY socket option)\n\
Options specific to -r:\n\
        -B      for -s, only output full blocks as specified by -l (for TAR)\n\
        -T      \"touch\": access each byte as it's read\n\
";

char stats[128];
unsigned long nbytes;           /* bytes on net */
unsigned long numCalls;         /* # of I/O system calls */
double cput, realt;             /* user, real time (seconds) */

void err (char *s);
void mes (char *s);
void pattern (register char *cp, register int cnt);
char *outfmt (double b);
static void getrusage (int ignored, register struct rusage *ru);
static void gettimeofday (struct timeval *tp, struct timezone *zp);
void prep_timer (void);
double read_timer (char *str, int len);
static void prusage (register struct rusage *r0, struct rusage *r1, struct timeval *e, struct timeval *b, char *outp);
static void tvadd (struct timeval *tsum, struct timeval *t0, struct timeval *t1);
static void tvsub (struct timeval *tdiff, struct timeval *t1, struct timeval *t0);
static void psecs (long l, register char *cp);
void delay (int us);
int mread (int fd, register char *bufp, unsigned n);
int Nread (int fd, char *buf, int count);
int Nwrite (int fd, char *buf, int count);

void
sigpipe ()
{
}

/* See <sys/socket.h> for details on this format */

struct sochdr 
{
  struct opthdr opthdr;
  long value;
} sochdr;

int
main (int argc, char *argv[])
{
  unsigned long addr_tmp;
  int c;
  struct t_optmgmt *so_options;
  struct t_optmgmt *so_options_ret;

  if (argc < 2)
    goto usage;

  while ((c = getopt (argc, argv, "drstuvBDTb:f:l:n:p:A:O:")) != -1)
    {
      switch (c)
        {

        case 'B':
          b_flag = 1;
          break;
        case 't':
          trans = 1;
          break;
        case 'r':
          trans = 0;
          break;
        case 'd':
          options |= SO_DEBUG;
          break;
        case 'D':
#ifdef TCP_NODELAY
          nodelay = 1;
#else
          fprintf (stderr,
                   "ttcp: -D option ignored: TCP_NODELAY socket option not supported\n");
#endif
          break;
        case 'n':
          nbuf = atoi (optarg);
          break;
        case 'l':
          buflen = atoi (optarg);
          break;
        case 's':
          sinkmode = !sinkmode;
          break;
        case 'p':
          port = atoi (optarg);
          break;
        case 'u':
          udp = 1;
          break;
        case 'v':
          verbose = 1;
          break;
        case 'A':
          bufalign = atoi (optarg);
          break;
        case 'O':
          bufoffset = atoi (optarg);
          break;
        case 'b':
#if defined(SO_SNDBUF) || defined(SO_RCVBUF)
          sockbufsize = atoi (optarg);
#else
          fprintf (stderr,
                   "ttcp: -b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported\n");
#endif
          break;
        case 'f':
          fmt = *optarg;
          break;
        case 'T':
          touchdata = 1;
          break;

        default:
          goto usage;
        }
    }
  if (trans)
    {
      /* xmitr */
      if (optind == argc)
        goto usage;
      memset ((char *) &sinhim, 0, sizeof (sinhim));
      host = argv[optind];
      if (atoi (host) > 0)
        {
          /* Numeric */
          sinhim.sin_family = AF_INET;
#if defined(cray)
          addr_tmp = inet_addr (host);
          sinhim.sin_addr = addr_tmp;
#else
          sinhim.sin_addr.s_addr = inet_addr (host);
#endif
        }
      else
        {
          if ((addr = gethostbyname (host)) == NULL)
            err ("bad hostname");
          sinhim.sin_family = addr->h_addrtype;
          memcpy ((char *) &addr_tmp, addr->h_addr, addr->h_length);
#if defined(cray)
          sinhim.sin_addr = addr_tmp;
#else
	  sinhim.sin_addr.s_addr = addr_tmp;
#endif /* cray */
        }

      sinhim.sin_port = htons (port);
      sinme.sin_family = AF_INET;
      sinme.sin_port = 0;       /* free choice */
    }
  else
    {
      /* rcvr */
      sinme.sin_family = AF_INET;
      sinme.sin_port = htons (port);
      sinme.sin_addr.s_addr = INADDR_ANY;
    }

  if (udp && buflen < 5)
    buflen = 5;               /* send more than the sentinel size */

  if ((buf = (char *) malloc (buflen + bufalign)) == (char *) NULL)
    err ("malloc");
  if (bufalign != 0)
    buf += (bufalign - ((int) buf % bufalign) + bufoffset) % bufalign;

  if (trans)
    {
      fprintf (stdout,
               "ttcp-t: buflen=%d, nbuf=%d, align=%d/%d, port=%d",
               buflen, nbuf, bufalign, bufoffset, port);
      if (sockbufsize)
        fprintf (stdout, ", sockbufsize=%d", sockbufsize);
      fprintf (stdout, "  %s  -> %s\n", udp ? "udp" : "tcp", host);
    }
  else
    {
      fprintf (stdout,
               "ttcp-r: buflen=%d, nbuf=%d, align=%d/%d, port=%d",
               buflen, nbuf, bufalign, bufoffset, port);
      if (sockbufsize)
        fprintf (stdout, ", sockbufsize=%d", sockbufsize);
      fprintf (stdout, "  %s\n", udp ? "udp" : "tcp");
    }

  if ((fd = t_open (udp ? "/dev/udp" : "/dev/tcp", O_RDWR, 0)) < 0)
    err ("socket");
  mes ("t_open");

  if ((so_options = (struct t_optmgmt *) t_alloc (fd, T_OPTMGMT, T_ALL)) == 0)
    err ("t_alloc");
  if ((so_options_ret = (struct t_optmgmt *) t_alloc (fd, T_OPTMGMT, T_ALL)) == 0)
    err ("t_alloc");    

  /* Set up options for TLI */
  so_options->opt.maxlen     = sizeof sochdr;
  so_options->opt.len        = sizeof sochdr;
  so_options->opt.buf        = (char *) &sochdr;
  so_options->flags          = T_NEGOTIATE;
  so_options_ret->opt.maxlen = sizeof sochdr;
  so_options_ret->opt.len    = sizeof sochdr;
  so_options_ret->opt.buf    = (char *) &sochdr;

  if (!trans) /* Server side */
    {
      struct t_bind req;
      req.addr.maxlen = sizeof sinme;
      req.addr.len    = sizeof sinme;
      req.addr.buf    = (char *) &sinme;
      req.qlen 	      = 1;
      
      if (t_bind (fd, &req, 0) == -1)
	err ("t_bind");
    }
  else /* Client side */
    if (t_bind (fd, 0, 0) == -1)
      err ("t_bind");

#if defined(SO_SNDBUF) || defined(SO_RCVBUF)
  if (sockbufsize)
    {
      if (trans)
        {
	  SET_SO_OPTION (sochdr, SOL_SOCKET, SO_SNDBUF, 4, sockbufsize);
	  if (t_optmgmt (fd, so_options, so_options_ret) == -1) 
            err ("t_optmgmt: sndbuf");
          mes ("sndbuf");
        }
      else
        {
	  SET_SO_OPTION (sochdr, SOL_SOCKET, SO_RCVBUF, 4, sockbufsize);
	  if (t_optmgmt (fd, so_options, so_options_ret) == -1) 
            err ("t_optmgmt: rcvbuf");
          mes ("rcvbuf");
        }
    }
#endif

  if (!udp)
    {
      signal (SIGPIPE, (SIG_TYP) sigpipe);
      if (trans)
        {
	  struct t_call call;
          /* We are the client if transmitting */
          if (options)
            {
	      SET_SO_OPTION (sochdr, SOL_SOCKET, options, 4, 1);
	      if (t_optmgmt (fd, so_options, so_options_ret) == -1) 
		err ("t_optmgmt: debug");
            }
#ifdef TCP_NODELAY
          if (nodelay)
            {
              struct protoent *p;
              p = getprotobyname ("tcp");
	      SET_SO_OPTION (sochdr, p->p_proto, TCP_NODELAY, 4, 1);
	      if (p && t_optmgmt (fd, so_options, so_options_ret) == -1) 
		err ("t_optmgmt: nodelay");
              mes ("nodelay");
            }
#endif
	  memset (&call, 0, sizeof call);
	  call.addr.maxlen = sizeof sinhim;
	  call.addr.len    = sizeof sinhim;
	  call.addr.buf	   = (char *) &sinhim;

          if (t_connect (fd, &call, 0) == -1)
	    {
	      if (t_errno == TLOOK)
		printf ("t_look = %d\n", t_look (fd));
	      else
		err ("t_connect");
	    }
          mes ("t_connect");
        }
      else
        {
          /* otherwise, we are the server and
	   * should listen for the connections
	   */

	  struct t_call *call_ptr;
	  if ((call_ptr = (struct t_call *) t_alloc (fd, T_CALL, T_ADDR)) == 0)
	    err ("t_alloc");
	
          if (options)
            {
	      SET_SO_OPTION (sochdr, SOL_SOCKET, options, 4, 1);
	      if (t_optmgmt (fd, so_options, so_options_ret) == -1) 
		err ("t_optmgmt: debug");
            }

          if (t_listen (fd, call_ptr) == -1)
	    err ("t_listen");

	  if (t_accept (fd, fd, call_ptr) == -1)
	    err ("t_accept");
	    
	  fprintf (stderr, "ttcp-r: t_accept from %s\n",
		   inet_ntoa (((struct sockaddr_in *) call_ptr->addr.buf)->sin_addr));
	}
    }

  (void) t_free ((char *) so_options, T_OPTMGMT);
  (void) t_free ((char *) so_options_ret, T_OPTMGMT);

  prep_timer ();
  errno = 0;
  if (sinkmode)
    {
      register int cnt;
      if (trans)
        {
          pattern (buf, buflen);
          if (udp)
            (void) Nwrite (fd, buf, 4); /* rcvr start */
          while (nbuf-- && Nwrite (fd, buf, buflen) == buflen)
            nbytes += buflen;
          if (udp)
            (void) Nwrite (fd, buf, 4); /* rcvr end */
        }
      else
        {
          if (udp)
            {
              while ((cnt = Nread (fd, buf, buflen)) > 0)
                {
                  static int going = 0;
                  if (cnt <= 4)
                    {
                      if (going)
                        break;  /* "EOF" */
                      going = 1;
                      prep_timer ();
                    }
                  else
		    nbytes += cnt;
                }
            }
          else
            {
              while ((cnt = Nread (fd, buf, buflen)) > 0)
		nbytes += cnt;
            }
        }
    }
  else
    {
      register int cnt;
      if (trans)
        {
          while ((cnt = read (0, buf, buflen)) > 0 &&
                 Nwrite (fd, buf, cnt) == cnt)
            nbytes += cnt;
        }
      else
        {
          while ((cnt = Nread (fd, buf, buflen)) > 0 &&
                 write (1, buf, cnt) == cnt)
            nbytes += cnt;
        }
    }
  if (errno)
    err ("IO");
  (void) read_timer (stats, sizeof (stats));
  if (udp && trans)
    {
      (void) Nwrite (fd, buf, 4);       /* rcvr end */
      (void) Nwrite (fd, buf, 4);       /* rcvr end */
      (void) Nwrite (fd, buf, 4);       /* rcvr end */
      (void) Nwrite (fd, buf, 4);       /* rcvr end */
    }
  if (cput <= 0.0)
    cput = 0.001;
  if (realt <= 0.0)
    realt = 0.001;
  fprintf (stdout,
           "ttcp%s: %ld bytes in %.2f real seconds = %s/sec +++\n",
           trans ? "-t" : "-r",
           nbytes, realt, outfmt (((double) nbytes) / realt));
  if (verbose)
    {
      fprintf (stdout,
               "ttcp%s: %ld bytes in %.2f CPU seconds = %s/cpu sec\n",
               trans ? "-t" : "-r",
               nbytes, cput, outfmt (((double) nbytes) / cput));
    }
  fprintf (stdout,
           "ttcp%s: %d I/O calls, msec/call = %.2f, calls/sec = %.2f\n",
           trans ? "-t" : "-r",
           numCalls,
           1024.0 * realt / ((double) numCalls),
           ((double) numCalls) / realt);
  fprintf (stdout, "ttcp%s: %s\n", trans ? "-t" : "-r", stats);
  if (verbose)
    {
      fprintf (stdout,
               "ttcp%s: buffer address %#x\n",
               trans ? "-t" : "-r",
               buf);
    }
  exit (0);

usage:
  fprintf (stderr, Usage);
  exit (1);
}

void
err (char *s)
{
  fprintf (stderr, "ttcp%s: ", trans ? "-t" : "-r");
  t_error (s);
  fprintf (stderr, "errno=%d, t_errno = %d\n", errno, t_errno);
  exit (1);
}

void
mes (char *s)
{
  fprintf (stderr, "ttcp%s: %s\n", trans ? "-t" : "-r", s);
}

void
pattern (register char *cp, register int cnt)
{
  register char c;
  c = 0;
  while (cnt-- > 0)
    {
      while (!isprint ((c & 0x7F)))
        c++;
      *cp++ = (c++ & 0x7F);
    }
}

char *
outfmt (double b)
{
  static char obuf[50];
  switch (fmt)
    {
    case 'G':
      sprintf (obuf, "%.2f GB", b / 1024.0 / 1024.0 / 1024.0);
      break;
    default:
    case 'K':
      sprintf (obuf, "%.2f KB", b / 1024.0);
      break;
    case 'M':
      sprintf (obuf, "%.2f MB", b / 1024.0 / 1024.0);
      break;
    case 'g':
      sprintf (obuf, "%.2f Gbit", b * 8.0 / 1024.0 / 1024.0 / 1024.0);
      break;
    case 'k':
      sprintf (obuf, "%.2f Kbit", b * 8.0 / 1024.0);
      break;
    case 'm':
      sprintf (obuf, "%.2f Mbit", b * 8.0 / 1024.0 / 1024.0);
      break;
    }
  return obuf;
}

static struct itimerval itime0;	/* Time at which timing started */
static struct rusage ru0;       /* Resource utilization at the start */

static void prusage ();
static void tvadd ();
static void tvsub ();
static void psecs ();

#if defined(SYSV)
/*ARGSUSED*/

static void
getrusage (int ignored, register struct rusage *ru)
{
  struct tms buf;

  times (&buf);

  /* Assumption: HZ <= 2147 (LONG_MAX/1000000) */
  ru->ru_stime.tv_sec = buf.tms_stime / HZ;
  ru->ru_stime.tv_usec = ((buf.tms_stime % HZ) * 1000000) / HZ;
  ru->ru_utime.tv_sec = buf.tms_utime / HZ;
  ru->ru_utime.tv_usec = ((buf.tms_utime % HZ) * 1000000) / HZ;
}

/*ARGSUSED*/
static void
gettimeofday (struct timeval *tp, struct timezone *zp)
{
  tp->tv_sec = time (0);
  tp->tv_usec = 0;
}

#endif /* SYSV */

/*
 *                    P R E P _ T I M E R
 * 
 */

void
prep_timer ()
{
  itime0.it_interval.tv_sec = 0;
  itime0.it_interval.tv_usec = 0;
  itime0.it_value.tv_sec = LONG_MAX / 22;	/* greatest possible value , itimer() count backwards */
  itime0.it_value.tv_usec = 0;


  getrusage (RUSAGE_SELF, &ru0);

  /* Init REAL Timer */
  if (setitimer (ITIMER_REAL, &itime0, NULL))
    {
      perror ("Setting 'itimer' REAL failed");
      return;
    }

}

/*
 *                    R E A D _ T I M E R
 * 
 */

double
read_timer (char *str, int len)
{
  struct itimerval itimedol;
  struct rusage ru1;
  struct timeval td;
  struct timeval tend, tstart;
  char line[132];

  getrusage (RUSAGE_SELF, &ru1);

  if (getitimer (ITIMER_REAL, &itimedol))
    {
      perror ("Getting 'itimer' REAL failed");
      return (0.0);
    }

  prusage (&ru0, &ru1, &itime0.it_value, &itimedol.it_value, line);
  (void) strncpy (str, line, len);

  /* Get real time */
  tvsub (&td, &itime0.it_value, &itimedol.it_value);
  realt = td.tv_sec + ((double) td.tv_usec) / 1000000;

  /* Get CPU time (user+sys) */
  tvadd (&tend, &ru1.ru_utime, &ru1.ru_stime);
  tvadd (&tstart, &ru0.ru_utime, &ru0.ru_stime);
  tvsub (&td, &tend, &tstart);
  cput = td.tv_sec + ((double) td.tv_usec) / 1000000;
  if (cput < 0.00001)
    cput = 0.00001;
  return (cput);
}

static void
prusage (register struct rusage *r0, struct rusage *r1, 
	 struct timeval *e, struct timeval *b, char *outp)
{
  struct timeval tdiff;
  register time_t t;
  register char *cp;
  register int i;
  int ms;

  t = (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 100 +
    (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 10000 +
    (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 100 +
    (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 10000;
  ms = (e->tv_sec - b->tv_sec) * 100 + (e->tv_usec - b->tv_usec) / 10000;

#define END(x)  {while(*x) x++;}
#if defined(SYSV)
  cp = "%Uuser %Ssys %Ereal %P";
#else
#if defined(sgi)                /* IRIX 3.3 will show 0 for %M,%F,%R,%C */
  cp = "%Uuser %Ssys %Ereal %P %Mmaxrss %F+%Rpf %Ccsw";
#else
  cp = "%Uuser %Ssys %Ereal %P %Xi+%Dd %Mmaxrss %F+%Rpf %Ccsw";
#endif
#endif
  for (; *cp; cp++)
    {
      if (*cp != '%')
        *outp++ = *cp;
      else if (cp[1])
        switch (*++cp)
          {

          case 'U':
            tvsub (&tdiff, &r1->ru_utime, &r0->ru_utime);
            sprintf (outp, "%d.%01d", tdiff.tv_sec, tdiff.tv_usec / 100000);
            END (outp);
            break;

          case 'S':
            tvsub (&tdiff, &r1->ru_stime, &r0->ru_stime);
            sprintf (outp, "%d.%01d", tdiff.tv_sec, tdiff.tv_usec / 100000);
            END (outp);
            break;

          case 'E':
            psecs (ms / 100, outp);
            END (outp);
            break;

          case 'P':
            sprintf (outp, "%d%%", (int) (t * 100 / ((ms ? ms : 1))));
            END (outp);
            break;

#if !defined(SYSV)
          case 'W':
            i = r1->ru_nswap - r0->ru_nswap;
            sprintf (outp, "%d", i);
            END (outp);
            break;

          case 'X':
            sprintf (outp, "%d", t == 0 ? 0 : (r1->ru_ixrss - r0->ru_ixrss) / t);
            END (outp);
            break;

          case 'D':
            sprintf (outp, "%d", t == 0 ? 0 :
                     (r1->ru_idrss + r1->ru_isrss - (r0->ru_idrss + r0->ru_isrss)) / t);
            END (outp);
            break;

          case 'K':
            sprintf (outp, "%d", t == 0 ? 0 :
                     ((r1->ru_ixrss + r1->ru_isrss + r1->ru_idrss) -
                      (r0->ru_ixrss + r0->ru_idrss + r0->ru_isrss)) / t);
            END (outp);
            break;

          case 'M':
            sprintf (outp, "%d", r1->ru_maxrss / 2);
            END (outp);
            break;

          case 'F':
            sprintf (outp, "%d", r1->ru_majflt - r0->ru_majflt);
            END (outp);
            break;

          case 'R':
            sprintf (outp, "%d", r1->ru_minflt - r0->ru_minflt);
            END (outp);
            break;

          case 'I':
            sprintf (outp, "%d", r1->ru_inblock - r0->ru_inblock);
            END (outp);
            break;

          case 'O':
            sprintf (outp, "%d", r1->ru_oublock - r0->ru_oublock);
            END (outp);
            break;
          case 'C':
            sprintf (outp, "%d+%d", r1->ru_nvcsw - r0->ru_nvcsw,
                     r1->ru_nivcsw - r0->ru_nivcsw);
            END (outp);
            break;
#endif /* !SYSV */
          }
    }
  *outp = '\0';
}

static void
tvadd (struct timeval *tsum, struct timeval *t0, struct timeval *t1)
{

  tsum->tv_sec = t0->tv_sec + t1->tv_sec;
  tsum->tv_usec = t0->tv_usec + t1->tv_usec;
  if (tsum->tv_usec > 1000000)
    tsum->tv_sec++, tsum->tv_usec -= 1000000;
}

static void
tvsub (struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
{

  tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
  tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
  if (tdiff->tv_usec < 0)
    tdiff->tv_sec--, tdiff->tv_usec += 1000000;
}

static void
psecs (long l, register char *cp)
{
  register int i;

  i = l / 3600;
  if (i)
    {
      sprintf (cp, "%d:", i);
      END (cp);
      i = l % 3600;
      sprintf (cp, "%d%d", (i / 60) / 10, (i / 60) % 10);
      END (cp);
    }
  else
    {
      i = l;
      sprintf (cp, "%d", i / 60);
      END (cp);
    }
  i %= 60;
  *cp++ = ':';
  sprintf (cp, "%d%d", i / 10, i % 10);
}

/*
 *                      N R E A D
 */
int
Nread (int fd, char *buf, int count)
{
  register int cnt;
  int flags = 0;

  if (udp)
    {
      struct sockaddr_in from;
      struct t_unitdata udata;

      udata.addr.maxlen  = sizeof from;
      udata.addr.buf     = (char *) &from;
      udata.udata.maxlen = count;
      udata.udata.buf    = (char *) buf;

      cnt = t_rcvudata (fd, &udata, &flags);
      if (cnt != -1)
	cnt = udata.udata.len;
      numCalls++;
    }
  else
    {
      if (b_flag)
        cnt = mread (fd, buf, count);   /* fill buf */
      else
        {
          cnt = t_rcv (fd, buf, count, &flags);
          numCalls++;
        }
      if (touchdata && cnt > 0)
        {
          register int c = cnt, sum;
          register char *b = buf;
          while (c--)
            sum += *b++;
        }
    }
  return (cnt);
}

/*
 *                      N W R I T E
 */

int
Nwrite (int fd, char *buf, int count)
{
  register int cnt;
  if (udp)
    {
      struct t_unitdata udata;

      memset (&udata, 0, sizeof udata);
      udata.addr.len     = sizeof sinhim;
      udata.addr.buf     = (char *) &sinhim;
      udata.udata.len    = count;
      udata.udata.buf    = (char *) buf;

    again:
      cnt = t_sndudata (fd, &udata);
      numCalls++;
      if (cnt == -1)
        {
	  if (errno == ENOBUFS)
	    {
	      delay (18000);
	      errno = 0;
	      goto again;
	    }
	  else
	    err ("t_sndudata");
	}
      else
	cnt = udata.udata.len;
    }
  else
    {
      cnt = t_snd (fd, buf, count, 0);
      numCalls++;
    }
  return (cnt);
}

void
delay (int us)
{
  struct timeval tv;

  tv.tv_sec = 0;
  tv.tv_usec = us;
  (void) select (1, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &tv);
}

/*
 *                      M R E A D
 *
 * This function performs the function of a read(II) but will
 * call read(II) multiple times in order to get the requested
 * number of characters.  This can be necessary because
 * network connections don't deliver data with the same
 * grouping as it is written with.  Written by Robert S. Miles, BRL.
 */
int
mread (int fd, register char *bufp, unsigned n)
{
  register unsigned count = 0;
  register int nread;
  int flags = 0;

  do
    {
      nread = t_rcv (fd, bufp, n - count, &flags);
      numCalls++;
      if (nread < 0)
        {
          perror ("ttcp_mread");
          return (-1);
        }
      if (nread == 0)
        return ((int) count);
      count += (unsigned) nread;
      bufp += nread;
  } while (count < n);

  return ((int) count);
}

/* 
        int             proc;
        char            buf[100];
        prusage_t       r;

        (void)sprintf(buf, "/proc/%d", getpid());
        if ((proc = open(buf, O_RDONLY, 0)) < 0) {
                fprintf(stderr, "open('%s'):", buf);
                perror("");
        } else if (ioctl(proc, PIOCUSAGE, (void *)&r) < 0)
                perror("ioctl(PIOCUSAGE)");
*/
-- 
Dr. Douglas C. Schmidt 			(schmidt@cs.wustl.edu)
Department of Computer Science, Washington University
St. Louis, MO 63130. Work #: (314) 935-7538; FAX #: (314) 035-7302
http://www.cs.wustl.edu/~schmidt