home *** CD-ROM | disk | FTP | other *** search
- /* :ex:se ts=4 sw=4 This program formatted with tabs 4 */
-
- /****************************************************
- * This software released to the public domain *
- * without restrictions of any kind. *
- ****************************************************/
-
- /*
- * NAME
- * perf - Test I/O system PERFormance.
- *
- * SYNOPSIS
- * perf [ -qsv ] [ -c command ] [ -e text ]
- * [ -t throttle ] [ -x outcount ] [ outcount ... ]
- *
- * DESCRIPTION
- * Perf measures the transmission of characters through
- * a communication media, keeping statistics on throughput
- * and system utilization.
- *
- * When the any of the options c, e, q, t or x, or an
- * outcount is specified, the program runs in output mode;
- * Otherwise it runs in input mode. A perf in output mode
- * may be used to test a system I/O subsystem output speed
- * directly, or its output may be fed through communication
- * hardware back into a perf in input mode for input
- * performance testing.
- *
- * -c shell_command Output the given shell command to be
- * executed by the receiving perf program.
- *
- * -e text Output the given text to be displayed
- * by the receiving perf program.
- *
- * -q Output code 99998, which tells the
- * receiving perf to terminate.
- *
- * -s Silent option. Suppress warning messages.
- *
- * -t throttle Throttle the input by sleeping for
- * one second every throttle characters.
- *
- * -v On input, print statistics showing
- * the length and number of times a
- * stream of characters was missed.
- *
- * -x outchar Output enough 6 digit + checksum
- * codes to produce the given number of
- * characters.
- *
- * outcount Same as -x outcount above.
- *
- * All data sent by an output perf can be interpreted by
- * an input perf, but the data is all printable ASCII in
- * a simple format so a human can understand it as well.
- *
- * To test system throughput, the perf program uses 6 digit
- * sequential codes, with a checksum. This format allows
- * the input program--or a human observer--to easily and
- * quantitatively compare desired to actual data.
- *
- * For input testing, it is often valuable to send data
- * from one system to another. In this mode, perf can
- * pass diagnostics text and shell commands through the
- * communications link. This allows the output system
- * to control the overall test procedure, and provide a
- * test log on the input system.
- *
- * To improve accuracy, on output a code of 99998 is output
- * for about 10K characters before measurement begins and
- * after measurement completes. This is done so the
- * measurement is more likely to be done under steady-state
- * conditions.
- *
- * WARNING
- * The order of parameters 2 & 3 on setvbuf() vary from
- * system, and are even inconsistent between the manuals
- * and the libraries on stock system V.2. You are wise
- * to check the order of the parameters on your system and
- * set the SETVBUF #define accordingly.
- */
-
- #if !defined(lint)
- char whatstring[] = "@(#)perf.c 3.1 9/22/89" ;
- #endif
-
- #if sun
- #define BSD 1
- #endif
-
- #if BSD
- # include <sys/types.h>
- # include <sys/time.h>
- # include <sys/resource.h>
- #else
- # include <sys/types.h>
- # include <sys/times.h>
- # include <sys/param.h>
- #endif
-
- #include <ctype.h>
- #include <stdio.h>
-
- #define uchar unsigned char
- #define ushort unsigned short
- #define ulong unsigned long
-
- extern char *optarg ;
- extern int optind ;
-
- extern char *ttyname() ;
- extern long atol() ;
- extern unsigned sleep() ;
-
- #if BSD
- extern char *sprintf() ;
- #else
- extern int sprintf() ;
- extern void exit() ;
- extern time_t times() ;
- #endif
-
-
- #if BSD
- # define TICS 1000 /* Fractional seconds */
- # define MS(tv) (1000000L / TICS * (tv).tv_sec + (tv).tv_usec / TICS)
- #else
- # define TICS HZ
- #endif
-
-
- #define NDIG 6 /* Number of digits/code */
- #define MAXCODE 999999 /* Largest NDIG number */
-
- #define NHEAD 10000 /* Number of header characters */
- #define NTRAIL 10000 /* Number of trailer characters */
-
- #define CPL 9 /* Number of codes per line */
- #define BASE 0x3f /* Base character for checksum */
-
- #define NCHAR(ncode) ((NDIG + 2) * (ncode) + 2 * (((ncode) + CPL - 1) / CPL))
- #define NCODE(nchar) (((nchar) * CPL) / (CPL * (NDIG + 2) + 2))
-
-
- char iobuf[3][BUFSIZ] ;
-
- /*
- * Command options.
- */
-
- int verbose ; /* Verbose error reporting */
- long throttle ; /* Max chars between sleep calls */
- int silent ; /* Silent option */
- int out ; /* Output test */
-
- /*
- * Misc globals.
- */
-
- int linepos ; /* Code position in line */
- char *tname ; /* Input/output ttyname */
-
- /*
- * Statistics gathering.
- */
-
- #define MAXSTAT 100 /* Gap max for statistics */
- int gap[MAXSTAT] ; /* Statistics on code gaps */
-
-
- /*************************************************************
- * Output a numeric code sequence *
- *************************************************************/
-
- outcode(v)
- register long v ;
- {
- register int ch ;
- register char *p ;
- static char digits[NDIG] ;
- static long lastv ;
-
- if (v == lastv)
- {
- p = &digits[NDIG] ;
- }
- else if (v == lastv + 1)
- {
- lastv++ ;
- for (p = digits ; *p == 9 ;) *p++ = 0 ;
- *p += 1 ;
- p = &digits[NDIG] ;
- }
- else
- {
- lastv = v ;
- for (p = digits ; p != &digits[NDIG] ;)
- {
- *p++ = v % 10 ;
- v /= 10 ;
- }
- }
-
- v = 0 ;
- while (p != &digits[0])
- {
- ch = *--p ;
- v += (v << 4) + ch ;
- ch += '0' ;
- (void) putc(ch, stdout) ;
- }
-
- ch = (v & 0x3f) + BASE ;
- (void) putc(ch, stdout) ;
-
- ch = ((v >> 6) & 0x3f) + BASE ;
- (void) putc(ch, stdout) ;
-
- if (++linepos == CPL)
- {
- (void) printf("\r\n") ;
- linepos = 0 ;
- }
- }
-
-
-
- /***************************************************************
- * Close out the current line *
- ***************************************************************/
-
- closeline()
- {
- if (linepos)
- {
- (void) printf("\r\n") ;
- linepos = 0 ;
- }
- }
-
-
-
- /************************************************************
- * Send text to a remote *
- ************************************************************/
-
- sendtext(prefix, text)
- int prefix ;
- char *text ;
- {
- int ch ;
- unsigned check ;
-
- closeline() ;
-
- for (;;)
- {
- check = 0 ;
- (void) fputc(prefix, stdout) ;
-
- while ((ch = *text++) && ch != '\n')
- {
- (void) fputc(ch, stdout) ;
- check = 17 * check + ch ;
- }
-
- (void) fputc((int)(check & 0x3f) + BASE, stdout) ;
- check >>= 6 ;
- (void) fputc((int)(check & 0x3f) + BASE, stdout) ;
-
- (void) printf("\r\n") ;
-
- if (ch == 0) break ;
- }
- }
-
-
-
- /***************************************************************
- * Transmit a given number of output codes *
- ***************************************************************/
-
- transmit(ncode)
- long ncode ;
- {
- long code ;
- ulong real ;
- ulong user ;
- ulong sys ;
- int cps ;
- char buf[200] ;
-
- #if BSD
- struct timeval tv ;
- struct rusage ru ;
- #else
- struct tms tms ;
- #endif
-
- /*
- * Output header codes to fill the output queue so we
- * will get a better idea of the true transmission times.
- */
-
- (void) printf("\r\n") ;
-
- linepos = 0 ;
- code = NCODE(NHEAD) ;
- while (--code >= 0) outcode((long) (MAXCODE - 1)) ;
- closeline() ;
- (void) fflush(stdout) ;
-
- /*
- * Get time statistics.
- */
-
- #if BSD
- (void) gettimeofday(&tv, (struct timezone *)0) ;
- real = MS(tv) ;
-
- (void) getrusage(RUSAGE_SELF, &ru) ;
- user = MS(ru.ru_utime) ;
- sys = MS(ru.ru_stime) ;
- #else
- real = times(&tms) ;
- user = tms.tms_utime ;
- sys = tms.tms_stime ;
- #endif
-
- /*
- * Output ncode actual codes.
- */
-
- for (code = 0 ; code < ncode ; code++) outcode(code) ;
- closeline() ;
- (void) fflush(stdout) ;
-
- /*
- * Figure the system resources used in the
- * duration of the test.
- */
-
- #if BSD
- (void) gettimeofday(&tv, (struct timezone *)0) ;
- real = MS(tv) - real ;
-
- (void) getrusage(RUSAGE_SELF, &ru) ;
- user = MS(ru.ru_utime) - user ;
- sys = MS(ru.ru_stime) - sys ;
- cps = 1000L * NCHAR(ncode) / (real ? real : 1) ;
- #else
- real = times(&tms) - real ;
- cps = TICS * NCHAR(ncode) / (real ? real : 1) ;
- user = tms.tms_utime - user ;
- sys = tms.tms_stime - sys ;
- #endif
-
- if (real == 0) real = 1 ;
-
- /*
- * Output trailer codes to end the sequence.
- */
-
- code = NCODE(NTRAIL) ;
- while (--code >= 0) outcode((long) (MAXCODE - 1)) ;
- closeline() ;
-
- (void) fflush(stdout) ;
-
- /*
- * Output results.
- */
-
- (void) sprintf(buf,
- "%s : OUT cps=%ld, real=%.2f, user=%.1f, sys=%.1f\n",
- tname, cps, (double) real / TICS,
- 100.0 * (double) user / (double) real,
- 100.0 * (double) sys / (double) real) ;
-
- (void) write(2, buf, (unsigned) strlen(buf)) ;
- }
-
-
- /***********************************************************
- * Honor a text escape from the remote. *
- ***********************************************************/
-
- readtext(prefix)
- int prefix ;
- {
- int ch ;
- int n ;
- int i ;
- int check ;
- char buf[1000] ;
-
- n = 0 ;
-
- for (;;)
- {
- if ((ch = fgetc(stdin)) == EOF) break ;
- ch &= 0x7f ;
-
- if (ch == '\r' || ch == '\n') break ;
-
- if (n < sizeof(buf))
- {
- buf[n] = ch ;
- }
- n++ ;
- }
-
- if (n < 2 || n > sizeof(buf))
- {
- (void) fprintf(stderr, "Text size error!\n", prefix) ;
- return ;
- }
-
- n -= 2 ;
- check = 0 ;
-
- for (i = 0 ; i < n ; i++) check = 17 * check + buf[i] ;
-
- if ( ((check & 0x3f) + BASE) != buf[n]
- || (((check >>= 6) & 0x3f) + BASE) != buf[n+1]
- )
- {
- (void) fprintf(stderr, "Text checksum error!\n", prefix) ;
- return ;
- }
-
- buf[n] = 0 ;
-
- if (prefix == '!') (void) system(buf) ;
-
- if (prefix == '#')
- {
- (void) fputs(buf, stderr) ;
- (void) fputc('\n', stderr) ;
- }
- }
-
-
-
- /***********************************************************
- * Receive data routine. *
- ***********************************************************/
-
- receive()
- {
- register int ch ;
- register long v ;
- int d ;
- int i ;
- int n ;
- int lch ;
- unsigned short s ;
- ulong real ;
- ulong user ;
- ulong sys ;
- long code ;
- long cps ;
- long error ;
- long trash ;
- long scode ;
- long incount ;
- char buf[200] ;
-
- #if BSD
- struct timeval tv ;
- struct rusage ru ;
- #else
- struct tms tms ;
- #endif
-
- real = 0 ;
- user = 0 ;
- sys = 0 ;
- trash = 0 ;
- error = 0 ;
- code = MAXCODE - 1 ;
- scode = 0 ;
- incount = 0 ;
-
- ch = getc(stdin) ;
-
- /*
- * Loop to read codes until and exit code appears.
- */
-
- for (;;)
- {
- v = 0 ;
- d = 0 ;
- s = 0 ;
-
- if (++incount >= throttle)
- {
- (void) sleep(1) ;
- incount = 0 ;
- }
-
- /*
- * Ignore white space, detect EOF, and scan
- * until a digit appears.
- */
-
- lch = '\n' ;
-
- for (;;)
- {
- if (ch == EOF)
- {
- if (!silent) (void) fprintf(stderr, "Exiting on EOF!\n") ;
- exit(1) ;
- }
-
- ch &= 0x7f ;
-
- if (ch >= '0' && ch <= '9') break ;
-
- if (lch == '\n' && (ch == '!' || ch == '#'))
- {
- readtext(ch) ;
- lch = '\n' ;
- }
- else if
- ( ch != '\r' && ch != '\n'
- && (ch < BASE || ch > (BASE + 0x3f))
- )
- trash++ ;
-
- lch = '\n' ;
- ch = getc(stdin) ;
- }
-
- /*
- * Pick up a digit string.
- */
-
- while (isdigit(ch))
- {
- ch -= '0' ;
- v = 10 * v + ch ;
- s += (s << 4) + ch ;
- d++ ;
- if ((ch = getc(stdin)) == EOF) break ;
- ch &= 0x7f ;
- }
-
- /*
- * Process checksum.
- */
-
- if (d != NDIG || (ch - BASE) != (s & 0x3f)) continue ;
-
- if ((ch = getc(stdin)) == EOF) continue ;
- ch &= 0x7f ;
-
- if ((ch - BASE) != ((s >> 6) & 0x3f)) continue ;
-
- ch = getc(stdin) ;
-
- /*
- * Pick up end of sequence or quit indicator.
- */
-
- if (v >= MAXCODE - 1)
- {
- if (code != MAXCODE - 1)
- {
- code -= scode ;
-
- /*
- * Figure resources used, and print results.
- */
-
- #if BSD
- (void) gettimeofday(&tv, (struct timezone *)0) ;
- real = MS(tv) - real ;
-
- (void) getrusage(RUSAGE_SELF, &ru) ;
- user = MS(ru.ru_utime) - user ;
- sys = MS(ru.ru_stime) - sys ;
- cps = 1000L * NCHAR(code) / (real ? real : 1) ;
- #else
- real = times(&tms) - real ;
- cps = TICS * NCHAR(code) / (real ? real : 1) ;
- user = tms.tms_utime - user ;
- sys = tms.tms_stime - sys ;
- #endif
- if (real == 0) real = 1 ;
-
- (void) sprintf(buf,
- "%s : IN cps=%ld, real=%.2f, user=%.1f, sys=%.1f, errs=%ld, stray=%ld\n",
- tname, cps, (double)real / TICS,
- 100.0 * (double) user / (double) real,
- 100.0 * (double) sys / (double) real,
- error, trash) ;
-
- (void) write(2, buf, (unsigned) strlen(buf)) ;
-
- /*
- * Print verbose error statistics.
- */
-
- if (verbose && error)
- {
- (void) fprintf(stderr,
- "%s : Sequence gaps were:\n\t", tname) ;
-
- n = 0 ;
- for (i = 0 ; i < MAXSTAT ; i++)
- {
- if (gap[i])
- {
- if (n == 6)
- {
- (void) fprintf(stderr, "\n\t") ;
- n = 0 ;
- }
- (void) fprintf(stderr,
- "%4d:%4d", NCHAR(i+1), gap[i]) ;
- n++ ;
- }
- }
- if (n) (void) fprintf(stderr, "\n") ;
- }
- }
-
- /*
- * Quit when the maximum input code is
- * received.
- */
-
- if (v == MAXCODE)
- {
- (void) fprintf(stderr,
- "%s : Received exit code, quitting\n", tname) ;
- exit(0) ;
- }
- if (code != MAXCODE - 1)
- {
- code = MAXCODE - 1 ;
- }
- }
-
- /*
- * Handle expected sequence number.
- */
-
- else if (v == code) code++ ;
-
- /*
- * Handle sequence lower than our current sequence,
- * indicating a restart.
- */
-
- else if (v < code)
- {
- if (code != MAXCODE - 1)
- {
- (void) fprintf(stderr,
- "%s : Incomplete sequence aborted\n", tname) ;
- }
-
- for (i = 0 ; i < MAXSTAT ; i++) gap[i] = 0 ;
-
- trash = 0 ;
- error = 0 ;
- code = v + 1 ;
-
- /*
- * Get start time statistics.
- */
-
- #if BSD
- (void) gettimeofday(&tv, (struct timezone *)0) ;
- real = MS(tv) ;
-
- (void) getrusage(RUSAGE_SELF, &ru) ;
- user = MS(ru.ru_utime) ;
- sys = MS(ru.ru_stime) ;
- #else
- real = times(&tms) ;
- user = tms.tms_utime ;
- sys = tms.tms_stime ;
- #endif
-
- scode = v ;
- }
-
- /*
- * Handle sequence greater than our expected
- * sequence.
- */
-
- else
- {
- i = v - code ;
- error += i ;
- if (i > MAXSTAT) i = MAXSTAT ;
- gap[i-1]++ ;
- code = v + 1 ;
- }
- }
- }
-
-
-
- /*************************************************************
- * Main Program *
- *************************************************************/
-
- main(argc, argv)
- int argc ;
- char **argv ;
- {
- int quit = 0 ;
- int i ;
- int ch ;
- long nchar ;
-
- #if SETVBUF
- (void) setvbuf(stdin, _IOFBF, iobuf[0], sizeof(iobuf[0])) ;
- (void) setvbuf(stdout, _IOFBF, iobuf[1], sizeof(iobuf[0])) ;
- (void) setvbuf(stderr, _IOLBF, iobuf[2], sizeof(iobuf[0])) ;
- #else
- (void) setvbuf(stdin, iobuf[0], _IOFBF, sizeof(iobuf[0])) ;
- (void) setvbuf(stdout, iobuf[1], _IOFBF, sizeof(iobuf[0])) ;
- (void) setvbuf(stderr, iobuf[2], _IOLBF, sizeof(iobuf[0])) ;
- #endif
-
- /*
- * Break out options.
- */
-
- throttle = 9999999 ;
-
- while ((ch = getopt(argc, argv, "c:e:qst:vx:")) != -1)
- {
- switch (ch)
- {
- case 'c':
- out++ ;
- sendtext('!', optarg) ;
- break ;
-
- case 'e':
- out++ ;
- sendtext('#', optarg) ;
- break ;
-
- case 'q':
- out++ ;
- quit++ ;
- break ;
-
- case 's':
- silent++ ;
- break ;
-
- case 't':
- throttle = atoi(optarg) ;
- throttle = NCODE(throttle) ;
- break ;
-
- case 'v':
- verbose++ ;
- break ;
-
- case 'x':
- out++ ;
- nchar = atoi(optarg) ;
- transmit(NCODE(nchar)) ;
- break ;
-
- default:
- (void) fprintf(stderr,
- "usage: %s [ -qv ] [ nchar ]\n", argv[0]) ;
- exit(2) ;
- }
- }
-
- /*
- * Check for terminal input/output.
- */
-
- if (optind != argc) out++ ;
-
- if (out)
- {
- tname = ttyname(1) ;
- if (tname == 0)
- {
- if (!silent)
- (void) fprintf(stderr, "Standard output is not a terminal!\n") ;
- tname = "OUTPUT" ;
- }
- }
- else
- {
- tname = ttyname(0) ;
- if (tname == 0)
- {
- if (!silent)
- (void) fprintf(stderr, "Standard input is not a terminal!\n") ;
- tname = "INPUT" ;
- }
- }
-
- /*
- * Output code counts as requested.
- */
-
- while (optind < argc)
- {
- nchar = atol(argv[optind]) ;
- if (nchar > 0) transmit(NCODE(nchar)) ;
- optind++ ;
- }
-
- /*
- * Output quit codes.
- */
-
- if (quit)
- {
- for (i = 0 ; i < NTRAIL ; i++)
- {
- outcode((long) MAXCODE) ;
- }
- closeline() ;
- }
-
- (void) fflush(stdout) ;
-
- /*
- * If no code counts or the quit flag, this is
- * a receive data test.
- */
-
- if (!out)
- {
- receive() ;
- exit(0) ;
- }
-
- return(0) ;
- }
-